diff options
author | Jim Blandy <jimb@redhat.com> | 2004-10-29 23:41:09 +0000 |
---|---|---|
committer | Jim Blandy <jimb@redhat.com> | 2004-10-29 23:41:09 +0000 |
commit | e57c60d93865368e0942b7de425e196870c1d6de (patch) | |
tree | 1f342aaafc4bd7d843d3f84f17d55d9a5c719900 | |
parent | a2e1331f84a93465abe229859bdee793e2eb8891 (diff) | |
download | gdb-e57c60d93865368e0942b7de425e196870c1d6de.tar.gz |
* linux-target.c: #include <string.h>, <sys/types.h>, and
<sys/wait.h> to get declarations for system functions used in this
file.
-rw-r--r-- | rda/unix/ChangeLog | 4 | ||||
-rw-r--r-- | rda/unix/linux-target.c | 3254 |
2 files changed, 3258 insertions, 0 deletions
diff --git a/rda/unix/ChangeLog b/rda/unix/ChangeLog index b46301088e9..892c83dcfc6 100644 --- a/rda/unix/ChangeLog +++ b/rda/unix/ChangeLog @@ -1,5 +1,9 @@ 2004-10-29 Jim Blandy <jimb@redhat.com> + * linux-target.c: #include <string.h>, <sys/types.h>, and + <sys/wait.h> to get declarations for system functions used in this + file. + * gdbserv-thread-db.h (stop_lwp, handle_waitstatus, ps_pdread, ps_pdwrite): Add declarations for these functions to gdbserv-thread-db.h. diff --git a/rda/unix/linux-target.c b/rda/unix/linux-target.c new file mode 100644 index 00000000000..c2312dc1e42 --- /dev/null +++ b/rda/unix/linux-target.c @@ -0,0 +1,3254 @@ +/* linux-target.c + + Copyright 2001, 2002 Red Hat, Inc. + + This file is part of RDA, the Red Hat Debug Agent (and library). + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Alternative licenses for RDA may be arranged by contacting Red Hat, + Inc. */ + +#include "config.h" + +#include <stdio.h> +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/wait.h> +#if !defined(_MIPSEL) && !defined(_MIPSEB) +#include <stdint.h> +#else +#include <asm/inst.h> +#endif + +#include <sys/ptrace.h> +#include <sys/procfs.h> +#include "gdbserv.h" +#include "gdbserv-target.h" +#include "gdbserv-utils.h" + +#include "gdb_proc_service.h" +#include "gdbserv-thread-db.h" + +#include "server.h" +#include "arch.h" +#include "ptrace-target.h" + +#ifdef STOCK_BREAKPOINTS +#include "stock-breakpoints.h" +#endif + +/* This is a linux native gdbserv target that uses the RDA library to + implement a remote gdbserver on a linux host. It controls the + process to be debugged on the linux host, allowing GDB to pull the + strings from any host on the network (or on a serial port). */ + + +/* Generic architecture stuff. */ +static struct arch * +allocate_empty_arch (void) +{ + struct arch *a = malloc (sizeof (*a)); + + memset (a, 0, sizeof (*a)); + + return a; +} + + + +/* + * Messy target-dependent register stuff + */ + +#if defined (HAVE_SYS_REG_H) +#include <sys/reg.h> +#elif defined (HAVE_ASM_REG_H) +#include <asm/reg.h> +#else +/* Desperation -- try asm/ptrace.h */ +#include <asm/ptrace.h> +#endif +#include <errno.h> +#include <string.h> + +enum +{ + /* Maximum size of a register in bytes. */ + MAX_REG_SIZE = 64 +}; + +enum regset +{ + /* Not available. */ + NOREGS, + + /* General purpose register. */ + GREGS, + + /* Floating point register. */ + FPREGS, + + /* Extended floating point register. (Probably MMX, SSE, or Altivec...) */ + FPXREGS, + + /* Other; not part of a regset. Must be fetched by other means. */ + OTHERREGS +}; + +/* struct getregs_setregs_reginfo is used to construct register tables + for architectures which need to use PTRACE_GETREGS, PTRACE_SETREGS, + PTRACE_GETFPREGS, PTRACE_SETFPREGS, etc. to access all of the + registers. */ + +struct getregs_setregs_reginfo +{ + /* The register set used to fetch this register, one of GREGS, FPREGS, + or FPXREGS. */ + enum regset whichregs; + + /* Register set specific offset needed for accessing the register + in question. */ + int offset; + + /* Size of the field being accessed in "ptrace" struct. */ + int ptrace_size; + + /* Size of field needed by gdb's remote protocol. */ + int proto_size; +}; + +/* struct peekuser_pokeuser_reginfo is used to construct register + tables for architectures which can access all of the registers + via the PTRACE_PEEKUSER and PTRACE_POKEUSER operations. */ + +struct peekuser_pokeuser_reginfo +{ + /* Offset to use with PTRACE_PEEKUSER or PTRACE_POKEUSER. This + offset should not require any further adjustment. */ + int ptrace_offset; + + /* Size of field being accessed via PTRACE_PEEKUSER or PTRACE_POKEUSER. + Note that a large enough value could cause multiple peek/poke + operations to occur. */ + int ptrace_size; + + /* Register set (either GREGS or FPREGS) that this register + belongs to. */ + enum regset whichregs; + + /* Offset into regset struct at which register is stored. */ + int regset_field_offset; + + /* Size of field in regset struct. */ + int regset_field_size; + + /* Size of field as required by gdb's remote protocol. */ + int proto_size; + + /* Some targets require the use of a different mechanism for fetching + the registers. If this field is non-zero, that different method will + be used. */ + int (*alternate_register_read_write_method) (struct gdbserv *, int pid, + int regno, void *read_buf, + const void *write_buf); +}; + +/* Obtain the offset of MEMBER from a struct of type TYPE. */ +#ifndef offsetof +#define offsetof(TYPE, MEMBER) ((int) ((char *) &((TYPE *) 0)->MEMBER)) +#endif + +/* Obtain the size of the field MEMBER from a struct of type TYPE. */ +#define fieldsize(TYPE, MEMBER) (sizeof (((TYPE *)0)->MEMBER)) + +/* Each architecture must define the following: + + 1) An anonymous enum which defines the number of registers + (NUM_REGS) accessible via a g/G packet. Note that this may be + larger or smaller than the total number of registers actually + available on an architecture. It can be larger in the case of + an architecture variant which, e.g, doesn't have floating point + registers. It could be smaller when there are certain control + registers which aren't exposed via GDB's debug interface. + + 2) An anonymous enum which defines the index of the PC register (PC_REGNUM). + + 3) A table named ``reginfo[]'' which describes the register set. Indices + into this table are GDB protocol register numbers. Elements of the + table are either peekuser_pokeuser_reginfo or getregs_setregs_reginfo + structs depending upon which ptrace() operations are required to + access all of the registers. Some ptrace() implementations contain + support for both {get,set}regset and {peek,poke}user operations, but + those which implement both sets of operations rarely provide support + for fetching all of the registers via one set of operations. Usually, + if the PTRACE_GETREGS operation is implemented, this means that + struct getregs_setregs_reginfo will need to be used to define reginfo[]. + Otherwise, peekuser_pokeuser_reginfo[] should be used. If it's the + case that both sets of operations provide complete access to all of + the registers of interest, then a slight preference should be given + to using the peekuser_pokeuser_reginfo struct since this code should + be somewhat more efficient. (Though this could change if a register + cache is implemented.) + + 4) Either + + #define PEEKUSER_POKEUSER_REGINFO 1 + + or + + #define GETREGS_SETREGS_REGINFO 1 + + Actually, there's a third option which is to define neither of + these, but this is only used by architectures which still use + the old, decrepit mechanism which doesn't work with threads + very well, nor handle registers of different sizes, etc. So, + do be sure to define one of the above. Someday, we'll hopefully + have all of the architectures converted over so that we won't + even need to mention this third option. + + 5) A function-like macro MAKE_ARCH (), expecting no arguments, which + expands to an expression that evaluates to a pointer to a 'struct + arch' object for the current architecture. We use this to + initialize the 'arch' member of the child_process structure. + + For now, this is optional; if not defined, child_process->arch is + set to zero. When we've converted all the architectures to + produce an arch object, we can remove the default, so new ports + that don't define a MAKE_ARCH macro will get an error, instead of + silently losing functionality. + +*/ + + + + +#if defined (ARM_LINUX_TARGET) + +/* ARM needs to use PTRACE_GETREGS / PTRACE_SETREGS and + PTRACE_GETFPREGS / PTRACE_SETFPREGS to access all of the registers. + */ +#define GETREGS_SETREGS_REGINFO 1 + +enum +{ + PC_REGNUM = 15, + NUM_REGS = 26, + sign_extend = 0 +}; + +static struct getregs_setregs_reginfo reginfo[] = +{ + { GREGS, 0 * 4, 4, 4 }, /* r0 */ + { GREGS, 1 * 4, 4, 4 }, /* r1 */ + { GREGS, 2 * 4, 4, 4 }, /* r2 */ + { GREGS, 3 * 4, 4, 4 }, /* r3 */ + { GREGS, 4 * 4, 4, 4 }, /* r4 */ + { GREGS, 5 * 4, 4, 4 }, /* r5 */ + { GREGS, 6 * 4, 4, 4 }, /* r6 */ + { GREGS, 7 * 4, 4, 4 }, /* r7 */ + { GREGS, 8 * 4, 4, 4 }, /* r8 */ + { GREGS, 9 * 4, 4, 4 }, /* r9 */ + { GREGS, 10 * 4, 4, 4 }, /* r10 */ + { GREGS, 11 * 4, 4, 4 }, /* r11 */ + { GREGS, 12 * 4, 4, 4 }, /* r12, sp */ + { GREGS, 13 * 4, 4, 4 }, /* r13, lr */ + { GREGS, 14 * 4, 4, 4 }, /* r14, fp */ + { GREGS, 15 * 4, 4, 4 }, /* r15, pc */ + { FPREGS, offsetof (struct user_fpregs, fpregs[0]), + fieldsize (struct user_fpregs, fpregs[0]), 12}, /* f0 */ + { FPREGS, offsetof (struct user_fpregs, fpregs[1]), + fieldsize (struct user_fpregs, fpregs[1]), 12}, /* f1 */ + { FPREGS, offsetof (struct user_fpregs, fpregs[2]), + fieldsize (struct user_fpregs, fpregs[2]), 12}, /* f2 */ + { FPREGS, offsetof (struct user_fpregs, fpregs[3]), + fieldsize (struct user_fpregs, fpregs[3]), 12}, /* f3 */ + { FPREGS, offsetof (struct user_fpregs, fpregs[4]), + fieldsize (struct user_fpregs, fpregs[4]), 12}, /* f4 */ + { FPREGS, offsetof (struct user_fpregs, fpregs[5]), + fieldsize (struct user_fpregs, fpregs[5]), 12}, /* f5 */ + { FPREGS, offsetof (struct user_fpregs, fpregs[6]), + fieldsize (struct user_fpregs, fpregs[6]), 12}, /* f6 */ + { FPREGS, offsetof (struct user_fpregs, fpregs[7]), + fieldsize (struct user_fpregs, fpregs[7]), 12}, /* f7 */ + /* We'd actually like to take the address of the fpsr field, but + unfortunately, this is a bitfield and it's not possible to take + its address. fpregs[8] *should* yield the same address. */ + { FPREGS, offsetof (struct user_fpregs, fpregs[8]), 4, 4}, /* fps */ + { GREGS, 16 * 4, 4, 4 } /* cpsr */ +}; + +/* End of ARM_LINUX_TARGET */ + +#elif defined (X86_LINUX_TARGET) + +/* X86 needs to use PTRACE_GETREGS / PTRACE_SETREGS, PTRACE_GETFPREGS / + PTRACE_SETFPREGS, and PTRACE_GETFPXREGS / PTRACE_SETFPXREGS to + access all of the registers. */ +#define GETREGS_SETREGS_REGINFO 1 + +enum +{ + PC_REGNUM = 8, + NUM_REGS = 42, + sign_extend = 0 +}; + + +static struct getregs_setregs_reginfo reginfo[] = +{ + { GREGS, EAX * 4, 4, 4 }, + { GREGS, ECX * 4, 4, 4 }, + { GREGS, EDX * 4, 4, 4 }, + { GREGS, EBX * 4, 4, 4 }, + { GREGS, UESP * 4, 4, 4 }, + { GREGS, EBP * 4, 4, 4 }, + { GREGS, ESI * 4, 4, 4 }, + { GREGS, EDI * 4, 4, 4 }, + { GREGS, EIP * 4, 4, 4 }, + { GREGS, EFL * 4, 4, 4 }, + { GREGS, CS * 4, 4, 4 }, + { GREGS, SS * 4, 4, 4 }, + { GREGS, DS * 4, 4, 4 }, + { GREGS, ES * 4, 4, 4 }, + { GREGS, FS * 4, 4, 4 }, + { GREGS, GS * 4, 4, 4 }, + /* 8 floating point registers */ + { FPREGS, offsetof (struct user_fpregs_struct, st_space[0]) + 0*10, 10, 10 }, + { FPREGS, offsetof (struct user_fpregs_struct, st_space[0]) + 1*10, 10, 10 }, + { FPREGS, offsetof (struct user_fpregs_struct, st_space[0]) + 2*10, 10, 10 }, + { FPREGS, offsetof (struct user_fpregs_struct, st_space[0]) + 3*10, 10, 10 }, + { FPREGS, offsetof (struct user_fpregs_struct, st_space[0]) + 4*10, 10, 10 }, + { FPREGS, offsetof (struct user_fpregs_struct, st_space[0]) + 5*10, 10, 10 }, + { FPREGS, offsetof (struct user_fpregs_struct, st_space[0]) + 6*10, 10, 10 }, + { FPREGS, offsetof (struct user_fpregs_struct, st_space[0]) + 7*10, 10, 10 }, + /* FCTRL */ + { FPREGS, offsetof (struct user_fpregs_struct, cwd), + fieldsize (struct user_fpregs_struct, cwd), 4}, + /* FSTAT */ + { FPREGS, offsetof (struct user_fpregs_struct, swd), + fieldsize (struct user_fpregs_struct, swd), 4}, + /* FTAG */ + { FPREGS, offsetof (struct user_fpregs_struct, twd), + fieldsize (struct user_fpregs_struct, twd), 4}, + /* FISEG or FCS */ + { FPREGS, offsetof (struct user_fpregs_struct, fcs), + fieldsize (struct user_fpregs_struct, fcs), 4}, + /* FIOFF or FCOFF */ + { FPREGS, offsetof (struct user_fpregs_struct, fip), + fieldsize (struct user_fpregs_struct, fip), 4}, + /* FOSEG or FDS */ + { FPREGS, offsetof (struct user_fpregs_struct, fos), + fieldsize (struct user_fpregs_struct, fos), 4}, + /* FOOFF or FDOFF */ + { FPREGS, offsetof (struct user_fpregs_struct, foo), + fieldsize (struct user_fpregs_struct, foo), 4}, + /* FOP */ + { FPXREGS, offsetof (struct user_fpxregs_struct, fop), + fieldsize (struct user_fpxregs_struct, fop), 4}, + /* 8 XMM registers */ + { FPXREGS, offsetof (struct user_fpxregs_struct, xmm_space[0 * 4]), 16, 16 }, + { FPXREGS, offsetof (struct user_fpxregs_struct, xmm_space[1 * 4]), 16, 16 }, + { FPXREGS, offsetof (struct user_fpxregs_struct, xmm_space[2 * 4]), 16, 16 }, + { FPXREGS, offsetof (struct user_fpxregs_struct, xmm_space[3 * 4]), 16, 16 }, + { FPXREGS, offsetof (struct user_fpxregs_struct, xmm_space[4 * 4]), 16, 16 }, + { FPXREGS, offsetof (struct user_fpxregs_struct, xmm_space[5 * 4]), 16, 16 }, + { FPXREGS, offsetof (struct user_fpxregs_struct, xmm_space[6 * 4]), 16, 16 }, + { FPXREGS, offsetof (struct user_fpxregs_struct, xmm_space[7 * 4]), 16, 16 }, + /* MXCSR */ + { FPXREGS, offsetof (struct user_fpxregs_struct, mxcsr), + fieldsize (struct user_fpxregs_struct, mxcsr), 4}, + /* ORIG_EAX - needed by gdb for signal handling. */ + { GREGS, ORIG_EAX * 4, 4, 4 } }; + + +/* Breakpoint methods for the x86. Except for bp_hit_p, these + are just wrappers for the stock breakpoint methods. In C++, we + could use multiple inheritance for this, and it would all just + work... */ + +/* x86 breakpoints tables are just stock breakpoint tables. But we + like static typechecking; casts swallow error messages. */ +static struct arch_bp_table * +stock_table_to_x86 (struct stock_bp_table *table) +{ + return (struct arch_bp_table *) table; +} + +static struct stock_bp_table * +x86_table_to_stock (struct arch_bp_table *table) +{ + return (struct stock_bp_table *) table; +} + +/* x86 breakpoints are just stock breakpoints. But we like static + typechecking; casts swallow error messages. */ +static struct arch_bp * +stock_bp_to_x86 (struct stock_bp *bp) +{ + return (struct arch_bp *) bp; +} + +static struct stock_bp * +x86_bp_to_stock (struct arch_bp *bp) +{ + return (struct stock_bp *) bp; +} + +struct arch_bp_table * +x86_make_bp_table (struct arch *arch, + struct gdbserv *serv, + struct gdbserv_target *target) +{ + struct stock_bp_table *table = stock_bp_make_table (serv, target); + + /* Use 'int 3' as the breakpoint instruction. */ + stock_bp_set_bp_insn (table, 1, "\xcc"); + + return stock_table_to_x86 (table); +} + + +static struct arch_bp * +x86_set_bp (struct arch_bp_table *table, + struct gdbserv_reg *addr) +{ + /* x86 arch breakpoints are just stock breakpoints. */ + return stock_bp_to_x86 (stock_bp_set_bp (x86_table_to_stock (table), + addr)); +} + + +static int +x86_delete_bp (struct arch_bp *bp) +{ + return stock_bp_delete_bp (x86_bp_to_stock (bp)); +} + + +static int +x86_bp_hit_p (struct gdbserv_thread *thread, + struct arch_bp *arch_bp) +{ + struct stock_bp *bp = x86_bp_to_stock (arch_bp); + struct stock_bp_table *table = stock_bp_table (bp); + struct gdbserv *serv = stock_bp_table_serv (table); + struct gdbserv_target *target = stock_bp_table_target (table); + struct gdbserv_reg bp_addr, pc; + unsigned long bp_addr_int, pc_int; + + stock_bp_addr (&bp_addr, bp); + gdbserv_reg_to_ulong (serv, &bp_addr, &bp_addr_int); + target->get_thread_reg (serv, thread, PC_REGNUM, &pc); + gdbserv_reg_to_ulong (serv, &pc, &pc_int); + + /* When the x86 hits a breakpoint, the reported PC is one greater + than the address of the breakpoint. */ + return bp_addr_int + 1 == pc_int; +} + + +/* Construct an architecture object for the x86. */ +static struct arch * +x86_make_arch (void) +{ + struct arch *a = malloc (sizeof (*a)); + + a->closure = 0; /* No closure needed at the moment. */ + a->make_bp_table = x86_make_bp_table; + a->set_bp = x86_set_bp; + a->delete_bp = x86_delete_bp; + a->bp_hit_p = x86_bp_hit_p; + + return a; +} + +#define MAKE_ARCH() (x86_make_arch ()) + +/* End of X86_LINUX_TARGET */ + +#elif defined (SH_LINUX_TARGET) + +/* Needs to be converted to use either GETREGS_SETREGS_REGINFO or + PEEKUSER_POKEUSER_REGINFO machinery. */ + +enum +{ + SIZEOF_REGMAP = 23, + SIZEOF_MAPPEDREG = 4, + NUM_REGS = 24, + PC_REGNUM = 16, + sign_extend = 0 +}; + +static int regmap[SIZEOF_REGMAP] = +{ + REG_REG0, REG_REG0+1, REG_REG0+2, REG_REG0+3, + REG_REG0+4, REG_REG0+5, REG_REG0+6, REG_REG0+7, + REG_REG0+8, REG_REG0+9, REG_REG0+10, REG_REG0+11, + REG_REG0+12, REG_REG0+13, REG_REG0+14, REG_REG15, + REG_PC, REG_PR, REG_GBR, -1, + REG_MACH, REG_MACL, REG_SR, +}; + +extern int +is_fp_reg (int regnum) +{ + return 0; +} + +int +is_gp_reg (int regnum) +{ + return !is_fp_reg (regnum); +} + +extern int +is_extended_reg (int regnum) +{ + return 0; +} + +/* End of SH_LINUX_TARGET */ + +#elif defined MIPS_LINUX_TARGET || (defined MIPS64_LINUX_TARGET && defined MIPS_ABI_O32) + +#define PEEKUSER_POKEUSER_REGINFO 1 + +enum +{ + NUM_REGS = 72, + PC_REGNUM = 37, + sign_extend = 1 +}; + +#ifndef FPR_BASE +#define FPR_BASE 32 +#endif +#ifndef PC +#define PC 64 +#endif +#ifndef CAUSE +#define CAUSE 65 +#endif +#ifndef BADVADDR +#define BADVADDR 66 +#endif +#ifndef MMHI +#define MMHI 67 +#endif +#ifndef MMLO +#define MMLO 68 +#endif +#ifndef FPC_CSR +#define FPC_CSR 69 +#endif +#ifndef FPC_EIR +#define FPC_EIR 70 +#endif + +#ifdef MIPS64_LINUX_TARGET +#define PROTO_SIZE 8 +#else +#define PROTO_SIZE 4 +#endif + +static struct peekuser_pokeuser_reginfo reginfo[] = +{ + /* MIPS has differing elf_gregset_t and gregset_t structs. (The + former contains some leading padding that the latter does not.) + elf_gregset_t is used to access registers from a core file whereas + gregset_t is used by the thread library in its interfaces. Since + we're concerned about the latter, we'll use the gregset_t offsets + in the table below. */ + { 0, 4, GREGS, 0 * 4, 4, PROTO_SIZE, 0 }, /* zero */ + { 1, 4, GREGS, 1 * 4, 4, PROTO_SIZE, 0 }, /* at */ + { 2, 4, GREGS, 2 * 4, 4, PROTO_SIZE, 0 }, /* v0 */ + { 3, 4, GREGS, 3 * 4, 4, PROTO_SIZE, 0 }, /* v1 */ + { 4, 4, GREGS, 4 * 4, 4, PROTO_SIZE, 0 }, /* a0 */ + { 5, 4, GREGS, 5 * 4, 4, PROTO_SIZE, 0 }, /* a1 */ + { 6, 4, GREGS, 6 * 4, 4, PROTO_SIZE, 0 }, /* a2 */ + { 7, 4, GREGS, 7 * 4, 4, PROTO_SIZE, 0 }, /* a3 */ + { 8, 4, GREGS, 8 * 4, 4, PROTO_SIZE, 0 }, /* t0 */ + { 9, 4, GREGS, 9 * 4, 4, PROTO_SIZE, 0 }, /* t1 */ + { 10, 4, GREGS, 10 * 4, 4, PROTO_SIZE, 0 }, /* t2 */ + { 11, 4, GREGS, 11 * 4, 4, PROTO_SIZE, 0 }, /* t3 */ + { 12, 4, GREGS, 12 * 4, 4, PROTO_SIZE, 0 }, /* t4 */ + { 13, 4, GREGS, 13 * 4, 4, PROTO_SIZE, 0 }, /* t5 */ + { 14, 4, GREGS, 14 * 4, 4, PROTO_SIZE, 0 }, /* t6 */ + { 15, 4, GREGS, 15 * 4, 4, PROTO_SIZE, 0 }, /* t7 */ + { 16, 4, GREGS, 16 * 4, 4, PROTO_SIZE, 0 }, /* s0 */ + { 17, 4, GREGS, 17 * 4, 4, PROTO_SIZE, 0 }, /* s1 */ + { 18, 4, GREGS, 18 * 4, 4, PROTO_SIZE, 0 }, /* s2 */ + { 19, 4, GREGS, 19 * 4, 4, PROTO_SIZE, 0 }, /* s3 */ + { 20, 4, GREGS, 20 * 4, 4, PROTO_SIZE, 0 }, /* s4 */ + { 21, 4, GREGS, 21 * 4, 4, PROTO_SIZE, 0 }, /* s5 */ + { 22, 4, GREGS, 22 * 4, 4, PROTO_SIZE, 0 }, /* s6 */ + { 23, 4, GREGS, 23 * 4, 4, PROTO_SIZE, 0 }, /* s7 */ + { 24, 4, GREGS, 24 * 4, 4, PROTO_SIZE, 0 }, /* t8 */ + { 25, 4, GREGS, 25 * 4, 4, PROTO_SIZE, 0 }, /* t9 */ + { 26, 4, GREGS, 26 * 4, 4, PROTO_SIZE, 0 }, /* k0 */ + { 27, 4, GREGS, 27 * 4, 4, PROTO_SIZE, 0 }, /* k1 */ + { 28, 4, GREGS, 28 * 4, 4, PROTO_SIZE, 0 }, /* gp */ + { 29, 4, GREGS, 29 * 4, 4, PROTO_SIZE, 0 }, /* sp */ + { 30, 4, GREGS, 30 * 4, 4, PROTO_SIZE, 0 }, /* s8/fp */ + { 31, 4, GREGS, 31 * 4, 4, PROTO_SIZE, 0 }, /* ra */ + { 0, 4, NOREGS, 0, 4, PROTO_SIZE, 0 }, /* sr */ + { MMLO, 4, GREGS, 33 * 4, 4, PROTO_SIZE, 0 }, /* lo */ + { MMHI, 4, GREGS, 32 * 4, 4, PROTO_SIZE, 0 }, /* hi */ + + /* glibc's ucontext.h doesn't specify the order of the following + three registerss. But there is space allocated for them. (Well, + for something, anyway - the g_pad[] array is has three elements.) + We use the same order for these fields as that specified in the + kernel header for elf_gregset_t; see the EF_ constants in + asm-mips/reg.h. Note, however, that the kernel header sandwiches + the status register (sr, above) in between ``bad'' and ``cause''. */ + +#if 0 + /* CAUSE and BADVADDR are readable via ptrace, but they're not writable. */ + { BADVADDR, 4, GREGS, 35 * 4, 4, PROTO_SIZE, 0 }, /* bad */ + { CAUSE, 4, GREGS, 36 * 4, 4, PROTO_SIZE, 0 }, /* cause */ +#else + { 0, 8, NOREGS, 0, 8, PROTO_SIZE, 0 }, /* bad */ + { 0, 8, NOREGS, 0, 8, PROTO_SIZE, 0 }, /* cause */ +#endif + { PC, 4, GREGS, 34 * 4, 4, PROTO_SIZE, 0 }, /* pc */ + + /* Linux/MIPS floating point is a bit of a mess. On the one hand, + the elf_fpregset_t contains space for 32 doubles plus the control + word. But on the other hand, the ptrace interface is only able to + fetch the 32 32-bit wide registers. This means that we only get + 16 double precision floats via ptrace(). It also means that only + slightly more than half of elf_fpregset_t is unused. */ + + { FPR_BASE + 0, 4, FPREGS, 0 * 4, 4, PROTO_SIZE, 0 }, /* $f0 */ + { FPR_BASE + 1, 4, FPREGS, 1 * 4, 4, PROTO_SIZE, 0 }, /* $f1 */ + { FPR_BASE + 2, 4, FPREGS, 2 * 4, 4, PROTO_SIZE, 0 }, /* $f2 */ + { FPR_BASE + 3, 4, FPREGS, 3 * 4, 4, PROTO_SIZE, 0 }, /* $f3 */ + { FPR_BASE + 4, 4, FPREGS, 4 * 4, 4, PROTO_SIZE, 0 }, /* $f4 */ + { FPR_BASE + 5, 4, FPREGS, 5 * 4, 4, PROTO_SIZE, 0 }, /* $f5 */ + { FPR_BASE + 6, 4, FPREGS, 6 * 4, 4, PROTO_SIZE, 0 }, /* $f6 */ + { FPR_BASE + 7, 4, FPREGS, 7 * 4, 4, PROTO_SIZE, 0 }, /* $f7 */ + { FPR_BASE + 8, 4, FPREGS, 8 * 4, 4, PROTO_SIZE, 0 }, /* $f8 */ + { FPR_BASE + 9, 4, FPREGS, 9 * 4, 4, PROTO_SIZE, 0 }, /* $f9 */ + { FPR_BASE + 10, 4, FPREGS, 10 * 4, 4, PROTO_SIZE, 0 }, /* $f10 */ + { FPR_BASE + 11, 4, FPREGS, 11 * 4, 4, PROTO_SIZE, 0 }, /* $f11 */ + { FPR_BASE + 12, 4, FPREGS, 12 * 4, 4, PROTO_SIZE, 0 }, /* $f12 */ + { FPR_BASE + 13, 4, FPREGS, 13 * 4, 4, PROTO_SIZE, 0 }, /* $f13 */ + { FPR_BASE + 14, 4, FPREGS, 14 * 4, 4, PROTO_SIZE, 0 }, /* $f14 */ + { FPR_BASE + 15, 4, FPREGS, 15 * 4, 4, PROTO_SIZE, 0 }, /* $f15 */ + { FPR_BASE + 16, 4, FPREGS, 16 * 4, 4, PROTO_SIZE, 0 }, /* $f16 */ + { FPR_BASE + 17, 4, FPREGS, 17 * 4, 4, PROTO_SIZE, 0 }, /* $f17 */ + { FPR_BASE + 18, 4, FPREGS, 18 * 4, 4, PROTO_SIZE, 0 }, /* $f18 */ + { FPR_BASE + 19, 4, FPREGS, 19 * 4, 4, PROTO_SIZE, 0 }, /* $f19 */ + { FPR_BASE + 20, 4, FPREGS, 20 * 4, 4, PROTO_SIZE, 0 }, /* $f20 */ + { FPR_BASE + 21, 4, FPREGS, 21 * 4, 4, PROTO_SIZE, 0 }, /* $f21 */ + { FPR_BASE + 22, 4, FPREGS, 22 * 4, 4, PROTO_SIZE, 0 }, /* $f22 */ + { FPR_BASE + 23, 4, FPREGS, 23 * 4, 4, PROTO_SIZE, 0 }, /* $f23 */ + { FPR_BASE + 24, 4, FPREGS, 24 * 4, 4, PROTO_SIZE, 0 }, /* $f24 */ + { FPR_BASE + 25, 4, FPREGS, 25 * 4, 4, PROTO_SIZE, 0 }, /* $f25 */ + { FPR_BASE + 26, 4, FPREGS, 26 * 4, 4, PROTO_SIZE, 0 }, /* $f26 */ + { FPR_BASE + 27, 4, FPREGS, 27 * 4, 4, PROTO_SIZE, 0 }, /* $f27 */ + { FPR_BASE + 28, 4, FPREGS, 28 * 4, 4, PROTO_SIZE, 0 }, /* $f28 */ + { FPR_BASE + 29, 4, FPREGS, 29 * 4, 4, PROTO_SIZE, 0 }, /* $f29 */ + { FPR_BASE + 30, 4, FPREGS, 30 * 4, 4, PROTO_SIZE, 0 }, /* $f30 */ + { FPR_BASE + 31, 4, FPREGS, 31 * 4, 4, PROTO_SIZE, 0 }, /* $f31 */ + { FPC_CSR, 4, FPREGS, 64 * 4, 4, PROTO_SIZE, 0 }, /* fsr */ + /* The "fir" value actually ends up occupying fp_pad in the fpregset + struct. */ + { FPC_EIR, 4, FPREGS, 65 * 4, 4, PROTO_SIZE, 0 } /* fir */ +}; + +static void mips_singlestep_program (struct gdbserv *serv); + +/* End of MIPS_LINUX_TARGET */ + +#elif defined(MIPS64_LINUX_TARGET) + +#define PEEKUSER_POKEUSER_REGINFO 1 + +enum +{ + NUM_REGS = 72, + PC_REGNUM = 37, + sign_extend = 1 +}; + + +static struct peekuser_pokeuser_reginfo reginfo[] = +{ + /* MIPS has differing elf_gregset_t and gregset_t structs. (The + former contains some leading padding that the latter does not.) + elf_gregset_t is used to access registers from a core file whereas + gregset_t is used by the thread library in its interfaces. Since + we're concerned about the latter, we'll use the gregset_t offsets + in the table below. */ + { 0, 8, GREGS, 0 * 8, 8, 8, 0 }, /* zero */ + { 1, 8, GREGS, 1 * 8, 8, 8, 0 }, /* at */ + { 2, 8, GREGS, 2 * 8, 8, 8, 0 }, /* v0 */ + { 3, 8, GREGS, 3 * 8, 8, 8, 0 }, /* v1 */ + { 4, 8, GREGS, 4 * 8, 8, 8, 0 }, /* a0 */ + { 5, 8, GREGS, 5 * 8, 8, 8, 0 }, /* a1 */ + { 6, 8, GREGS, 6 * 8, 8, 8, 0 }, /* a2 */ + { 7, 8, GREGS, 7 * 8, 8, 8, 0 }, /* a3 */ + { 8, 8, GREGS, 8 * 8, 8, 8, 0 }, /* t0 */ + { 9, 8, GREGS, 9 * 8, 8, 8, 0 }, /* t1 */ + { 10, 8, GREGS, 10 * 8, 8, 8, 0 }, /* t2 */ + { 11, 8, GREGS, 11 * 8, 8, 8, 0 }, /* t3 */ + { 12, 8, GREGS, 12 * 8, 8, 8, 0 }, /* t4 */ + { 13, 8, GREGS, 13 * 8, 8, 8, 0 }, /* t5 */ + { 14, 8, GREGS, 14 * 8, 8, 8, 0 }, /* t6 */ + { 15, 8, GREGS, 15 * 8, 8, 8, 0 }, /* t7 */ + { 16, 8, GREGS, 16 * 8, 8, 8, 0 }, /* s0 */ + { 17, 8, GREGS, 17 * 8, 8, 8, 0 }, /* s1 */ + { 18, 8, GREGS, 18 * 8, 8, 8, 0 }, /* s2 */ + { 19, 8, GREGS, 19 * 8, 8, 8, 0 }, /* s3 */ + { 20, 8, GREGS, 20 * 8, 8, 8, 0 }, /* s4 */ + { 21, 8, GREGS, 21 * 8, 8, 8, 0 }, /* s5 */ + { 22, 8, GREGS, 22 * 8, 8, 8, 0 }, /* s6 */ + { 23, 8, GREGS, 23 * 8, 8, 8, 0 }, /* s7 */ + { 24, 8, GREGS, 24 * 8, 8, 8, 0 }, /* t8 */ + { 25, 8, GREGS, 25 * 8, 8, 8, 0 }, /* t9 */ + { 26, 8, GREGS, 26 * 8, 8, 8, 0 }, /* k0 */ + { 27, 8, GREGS, 27 * 8, 8, 8, 0 }, /* k1 */ + { 28, 8, GREGS, 28 * 8, 8, 8, 0 }, /* gp */ + { 29, 8, GREGS, 29 * 8, 8, 8, 0 }, /* sp */ + { 30, 8, GREGS, 30 * 8, 8, 8, 0 }, /* s8/fp */ + { 31, 8, GREGS, 31 * 8, 8, 8, 0 }, /* ra */ + { 0, 8, NOREGS, 0, 8, 8, 0 }, /* sr */ + { 68, 8, GREGS, 33 * 8, 8, 8, 0 }, /* lo */ + { 67, 8, GREGS, 32 * 8, 8, 8, 0 }, /* hi */ + + /* glibc's ucontext.h doesn't specify the order of the following + three registerss. But there is space allocated for them. (Well, + for something, anyway - the g_pad[] array is has three elements.) + We use the same order for these fields as that specified in the + kernel header for elf_gregset_t; see the EF_ constants in + asm-mips/reg.h. Note, however, that the kernel header sandwiches + the status register (sr, above) in between ``bad'' and ``cause''. */ + +#if 0 + /* CAUSE and BADVADDR are readable via ptrace, but they're not writable. */ + { 66, 8, GREGS, 35 * 8, 8, 8, 0 }, /* bad */ + { 65, 8, GREGS, 36 * 8, 8, 8, 0 }, /* cause */ +#else + { 0, 8, NOREGS, 0, 8, 8, 0 }, /* bad */ + { 0, 8, NOREGS, 0, 8, 8, 0 }, /* cause */ +#endif + { 64, 8, GREGS, 34 * 8, 8, 8, 0 }, /* pc */ + + /* Linux/MIPS floating point is a bit of a mess. On the one hand, + the elf_fpregset_t contains space for 32 doubles plus the control + word. But on the other hand, the ptrace interface is only able to + fetch the 32 32-bit wide registers. This means that we only get + 16 double precision floats via ptrace(). It also means that only + slightly more than half of elf_fpregset_t is unused. */ + + { 32 + 0, 8, FPREGS, 0 * 8, 8, 8, 0 }, /* $f0 */ + { 32 + 1, 8, FPREGS, 1 * 8, 8, 8, 0 }, /* $f1 */ + { 32 + 2, 8, FPREGS, 2 * 8, 8, 8, 0 }, /* $f2 */ + { 32 + 3, 8, FPREGS, 3 * 8, 8, 8, 0 }, /* $f3 */ + { 32 + 4, 8, FPREGS, 4 * 8, 8, 8, 0 }, /* $f4 */ + { 32 + 5, 8, FPREGS, 5 * 8, 8, 8, 0 }, /* $f5 */ + { 32 + 6, 8, FPREGS, 6 * 8, 8, 8, 0 }, /* $f6 */ + { 32 + 7, 8, FPREGS, 7 * 8, 8, 8, 0 }, /* $f7 */ + { 32 + 8, 8, FPREGS, 8 * 8, 8, 8, 0 }, /* $f8 */ + { 32 + 9, 8, FPREGS, 9 * 8, 8, 8, 0 }, /* $f9 */ + { 32 + 10, 8, FPREGS, 10 * 8, 8, 8, 0 }, /* $f10 */ + { 32 + 11, 8, FPREGS, 11 * 8, 8, 8, 0 }, /* $f11 */ + { 32 + 12, 8, FPREGS, 12 * 8, 8, 8, 0 }, /* $f12 */ + { 32 + 13, 8, FPREGS, 13 * 8, 8, 8, 0 }, /* $f13 */ + { 32 + 14, 8, FPREGS, 14 * 8, 8, 8, 0 }, /* $f14 */ + { 32 + 15, 8, FPREGS, 15 * 8, 8, 8, 0 }, /* $f15 */ + { 32 + 16, 8, FPREGS, 16 * 8, 8, 8, 0 }, /* $f16 */ + { 32 + 17, 8, FPREGS, 17 * 8, 8, 8, 0 }, /* $f17 */ + { 32 + 18, 8, FPREGS, 18 * 8, 8, 8, 0 }, /* $f18 */ + { 32 + 19, 8, FPREGS, 19 * 8, 8, 8, 0 }, /* $f19 */ + { 32 + 20, 8, FPREGS, 20 * 8, 8, 8, 0 }, /* $f20 */ + { 32 + 21, 8, FPREGS, 21 * 8, 8, 8, 0 }, /* $f21 */ + { 32 + 22, 8, FPREGS, 22 * 8, 8, 8, 0 }, /* $f22 */ + { 32 + 23, 8, FPREGS, 23 * 8, 8, 8, 0 }, /* $f23 */ + { 32 + 24, 8, FPREGS, 24 * 8, 8, 8, 0 }, /* $f24 */ + { 32 + 25, 8, FPREGS, 25 * 8, 8, 8, 0 }, /* $f25 */ + { 32 + 26, 8, FPREGS, 26 * 8, 8, 8, 0 }, /* $f26 */ + { 32 + 27, 8, FPREGS, 27 * 8, 8, 8, 0 }, /* $f27 */ + { 32 + 28, 8, FPREGS, 28 * 8, 8, 8, 0 }, /* $f28 */ + { 32 + 29, 8, FPREGS, 29 * 8, 8, 8, 0 }, /* $f29 */ + { 32 + 30, 8, FPREGS, 30 * 8, 8, 8, 0 }, /* $f30 */ + { 32 + 31, 8, FPREGS, 31 * 8, 8, 8, 0 }, /* $f31 */ + { 69, 8, FPREGS, 32 * 8, 4, 8, 0 }, /* fsr */ + /* The "fir" value actually ends up occupying fp_pad in the fpregset + struct. */ + { 70, 8, FPREGS, 33 * 8 + 4, 4, 8, 0 } /* fir */ +}; + +static void mips_singlestep_program (struct gdbserv *serv); + +/* End of MIPS64_LINUX_TARGET */ + +#elif M68K_LINUX_TARGET + +/* Needs to be converted to use either GETREGS_SETREGS_REGINFO or + PEEKUSER_POKEUSER_REGINFO machinery. */ + +#error m68k +enum +{ + SIZEOF_REGMAP = 29, /* with FP regs */ + SIZEOF_MAPPEDREG = 4, + NUM_REGS = 29, + PC_REGNUM = 17, + sign_extend = 0 +}; + +static int regmap[SIZEOF_REGMAP] = +{ + PT_D0, PT_D1, PT_D2, PT_D3, PT_D4, PT_D5, PT_D6, PT_D7, + PT_A0, PT_A1, PT_A2, PT_A3, PT_A4, PT_A5, PT_A6, PT_USP, + PT_SR, PT_PC, +#if defined (PT_FP0) + PT_FP0, PT_FP1, PT_FP2, PT_FP3, PT_FP4, PT_FP5, PT_FP6, PT_FP7, + PT_FPCR, PT_FPSR, PT_FPIAR +#else + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1 +#endif /* floating point regs */ +}; + +extern int +is_fp_reg (int regnum) +{ + if (regnum < SIZEOF_REGMAP) + { + switch (regmap[regnum]) { +#if defined (PT_FP0) + case PT_FP0: case PT_FP1: case PT_FP2: case PT_FP3: + case PT_FP4: case PT_FP5: case PT_FP6: case PT_FP7: + case PT_FPCR: case PT_FPSR: case PT_FPIAR: + return 1; +#endif + default: + return 0; + } + } + return 0; +} + +int +is_gp_reg (int regnum) +{ + return !is_fp_reg (regnum); +} + +int +is_extended_reg (int regnum) +{ + return 0; +} + +/* End of M68_LINUX_TARGET */ + +#elif defined (PPC_LINUX_TARGET) + +#define PEEKUSER_POKEUSER_REGINFO 1 + +enum +{ + NUM_REGS = 71, + PC_REGNUM = 64, + sign_extend = 0 +}; + +static struct peekuser_pokeuser_reginfo reginfo[] = +{ + { PT_R0 * 4, 4, GREGS, PT_R0 * 4, 4, 4, 0 }, + { PT_R1 * 4, 4, GREGS, PT_R1 * 4, 4, 4, 0 }, + { PT_R2 * 4, 4, GREGS, PT_R2 * 4, 4, 4, 0 }, + { PT_R3 * 4, 4, GREGS, PT_R3 * 4, 4, 4, 0 }, + { PT_R4 * 4, 4, GREGS, PT_R4 * 4, 4, 4, 0 }, + { PT_R5 * 4, 4, GREGS, PT_R5 * 4, 4, 4, 0 }, + { PT_R6 * 4, 4, GREGS, PT_R6 * 4, 4, 4, 0 }, + { PT_R7 * 4, 4, GREGS, PT_R7 * 4, 4, 4, 0 }, + { PT_R8 * 4, 4, GREGS, PT_R8 * 4, 4, 4, 0 }, + { PT_R9 * 4, 4, GREGS, PT_R9 * 4, 4, 4, 0 }, + { PT_R10 * 4, 4, GREGS, PT_R10 * 4, 4, 4, 0 }, + { PT_R11 * 4, 4, GREGS, PT_R11 * 4, 4, 4, 0 }, + { PT_R12 * 4, 4, GREGS, PT_R12 * 4, 4, 4, 0 }, + { PT_R13 * 4, 4, GREGS, PT_R13 * 4, 4, 4, 0 }, + { PT_R14 * 4, 4, GREGS, PT_R14 * 4, 4, 4, 0 }, + { PT_R15 * 4, 4, GREGS, PT_R15 * 4, 4, 4, 0 }, + { PT_R16 * 4, 4, GREGS, PT_R16 * 4, 4, 4, 0 }, + { PT_R17 * 4, 4, GREGS, PT_R17 * 4, 4, 4, 0 }, + { PT_R18 * 4, 4, GREGS, PT_R18 * 4, 4, 4, 0 }, + { PT_R19 * 4, 4, GREGS, PT_R19 * 4, 4, 4, 0 }, + { PT_R20 * 4, 4, GREGS, PT_R20 * 4, 4, 4, 0 }, + { PT_R21 * 4, 4, GREGS, PT_R21 * 4, 4, 4, 0 }, + { PT_R22 * 4, 4, GREGS, PT_R22 * 4, 4, 4, 0 }, + { PT_R23 * 4, 4, GREGS, PT_R23 * 4, 4, 4, 0 }, + { PT_R24 * 4, 4, GREGS, PT_R24 * 4, 4, 4, 0 }, + { PT_R25 * 4, 4, GREGS, PT_R25 * 4, 4, 4, 0 }, + { PT_R26 * 4, 4, GREGS, PT_R26 * 4, 4, 4, 0 }, + { PT_R27 * 4, 4, GREGS, PT_R27 * 4, 4, 4, 0 }, + { PT_R28 * 4, 4, GREGS, PT_R28 * 4, 4, 4, 0 }, + { PT_R29 * 4, 4, GREGS, PT_R29 * 4, 4, 4, 0 }, + { PT_R30 * 4, 4, GREGS, PT_R30 * 4, 4, 4, 0 }, + { PT_R31 * 4, 4, GREGS, PT_R31 * 4, 4, 4, 0 }, + { (PT_FPR0 + 0) * 4, 8, FPREGS, 0 * 4, 8, 8, 0 }, + { (PT_FPR0 + 2) * 4, 8, FPREGS, 2 * 4, 8, 8, 0 }, + { (PT_FPR0 + 4) * 4, 8, FPREGS, 4 * 4, 8, 8, 0 }, + { (PT_FPR0 + 6) * 4, 8, FPREGS, 6 * 4, 8, 8, 0 }, + { (PT_FPR0 + 8) * 4, 8, FPREGS, 8 * 4, 8, 8, 0 }, + { (PT_FPR0 + 10) * 4, 8, FPREGS, 10 * 4, 8, 8, 0 }, + { (PT_FPR0 + 12) * 4, 8, FPREGS, 12 * 4, 8, 8, 0 }, + { (PT_FPR0 + 14) * 4, 8, FPREGS, 14 * 4, 8, 8, 0 }, + { (PT_FPR0 + 16) * 4, 8, FPREGS, 16 * 4, 8, 8, 0 }, + { (PT_FPR0 + 18) * 4, 8, FPREGS, 18 * 4, 8, 8, 0 }, + { (PT_FPR0 + 20) * 4, 8, FPREGS, 20 * 4, 8, 8, 0 }, + { (PT_FPR0 + 22) * 4, 8, FPREGS, 22 * 4, 8, 8, 0 }, + { (PT_FPR0 + 24) * 4, 8, FPREGS, 24 * 4, 8, 8, 0 }, + { (PT_FPR0 + 26) * 4, 8, FPREGS, 26 * 4, 8, 8, 0 }, + { (PT_FPR0 + 28) * 4, 8, FPREGS, 28 * 4, 8, 8, 0 }, + { (PT_FPR0 + 30) * 4, 8, FPREGS, 30 * 4, 8, 8, 0 }, + { (PT_FPR0 + 32) * 4, 8, FPREGS, 32 * 4, 8, 8, 0 }, + { (PT_FPR0 + 34) * 4, 8, FPREGS, 34 * 4, 8, 8, 0 }, + { (PT_FPR0 + 36) * 4, 8, FPREGS, 36 * 4, 8, 8, 0 }, + { (PT_FPR0 + 38) * 4, 8, FPREGS, 38 * 4, 8, 8, 0 }, + { (PT_FPR0 + 40) * 4, 8, FPREGS, 40 * 4, 8, 8, 0 }, + { (PT_FPR0 + 42) * 4, 8, FPREGS, 42 * 4, 8, 8, 0 }, + { (PT_FPR0 + 44) * 4, 8, FPREGS, 44 * 4, 8, 8, 0 }, + { (PT_FPR0 + 46) * 4, 8, FPREGS, 46 * 4, 8, 8, 0 }, + { (PT_FPR0 + 48) * 4, 8, FPREGS, 48 * 4, 8, 8, 0 }, + { (PT_FPR0 + 50) * 4, 8, FPREGS, 50 * 4, 8, 8, 0 }, + { (PT_FPR0 + 52) * 4, 8, FPREGS, 52 * 4, 8, 8, 0 }, + { (PT_FPR0 + 54) * 4, 8, FPREGS, 54 * 4, 8, 8, 0 }, + { (PT_FPR0 + 56) * 4, 8, FPREGS, 56 * 4, 8, 8, 0 }, + { (PT_FPR0 + 58) * 4, 8, FPREGS, 58 * 4, 8, 8, 0 }, + { (PT_FPR0 + 60) * 4, 8, FPREGS, 60 * 4, 8, 8, 0 }, + { (PT_FPR0 + 62) * 4, 8, FPREGS, 62 * 4, 8, 8, 0 }, + { PT_NIP * 4, 4, GREGS, PT_NIP * 4, 4, 4, 0 }, + { PT_MSR * 4, 4, GREGS, PT_MSR * 4, 4, 4, 0 }, + { PT_CCR * 4, 4, GREGS, PT_CCR * 4, 4, 4, 0 }, + { PT_LNK * 4, 4, GREGS, PT_LNK * 4, 4, 4, 0 }, + { PT_CTR * 4, 4, GREGS, PT_CTR * 4, 4, 4, 0 }, + { PT_XER * 4, 4, GREGS, PT_XER * 4, 4, 4, 0 } +#ifdef PT_MQ +, { PT_MQ * 4, 4, GREGS, PT_MQ * 4, 4, 4, 0 } +#endif +}; + +/* End of PPC_LINUX_TARGET */ +#elif defined (ALPHA_LINUX_TARGET) + +/* Needs to be converted to use either GETREGS_SETREGS_REGINFO or + PEEKUSER_POKEUSER_REGINFO machinery. */ + +enum +{ + SIZEOF_REGMAP = 66, + SIZEOF_MAPPEDREG = 8, + NUM_REGS = 66, + PC_REGNUM = 64, + sign_extend = 0 +}; + +static int regmap[SIZEOF_REGMAP] = +{ + EF_V0, EF_T0, EF_T1, EF_T2, EF_T3, EF_T4, EF_T5, EF_T6, + EF_T7, EF_S0, EF_S1, EF_S2, EF_S3, EF_S4, EF_S5, EF_S6, + EF_A0, EF_A1, EF_A2, EF_A3, EF_A4, EF_A5, EF_T8, EF_T9, + EF_T10, EF_T11, EF_RA, EF_T12, EF_AT, EF_GP, EF_SP, -1, /* zero */ + /* f0 f1 f2 f3 f4 f5 f6 f7 */ + -1, -1, -1, -1, -1, -1, -1, -1, + /* f8 f9 f10 f11 f12 f13 f14 f15 */ + -1, -1, -1, -1, -1, -1, -1, -1, + /* f16 f17 f18 f19 f20 f21 f22 f23 */ + -1, -1, -1, -1, -1, -1, -1, -1, + /* f24 f25 f26 f27 f28 f29 f30 fpcr */ + -1, -1, -1, -1, -1, -1, -1, -1, + EF_PC, -1 /* vpf */ +}; + +int +is_fp_reg (int regnum) +{ + if (regnum >= 32 && regnum < 64) + return 1; + else + return 0; +} + +int +is_gp_reg (int regnum) +{ + return !is_fp_reg (regnum); +} + +int +is_extended_reg (int regnum) +{ + return 0; +} + +/* End of ALPHA_LINUX_TARGET */ +#elif defined(FRV_LINUX_TARGET) + +#define PEEKUSER_POKEUSER_REGINFO 1 + +enum +{ + NUM_REGS = 149, + PC_REGNUM = 128, + sign_extend = 0 +}; + +#define greg_offset_and_size(FIELD) GREGS, offsetof (struct user_int_regs, FIELD), fieldsize (struct user_int_regs, FIELD) +#define fpreg_offset_and_size(FIELD) FPREGS, offsetof (struct user_fpmedia_regs, FIELD), fieldsize (struct user_fpmedia_regs, FIELD) +#define noreg_offset_and_size(FIELD) NOREGS, 0, 0 + +static int frv_fdpic_loadmap_addresses (struct gdbserv *, int, int, void *, + const void *); + +static struct peekuser_pokeuser_reginfo reginfo[] = +{ + { PT_GR(0) * 4, 4, greg_offset_and_size (gr[0]), 4, 0 }, + { PT_GR(1) * 4, 4, greg_offset_and_size (gr[1]), 4, 0 }, + { PT_GR(2) * 4, 4, greg_offset_and_size (gr[2]), 4, 0 }, + { PT_GR(3) * 4, 4, greg_offset_and_size (gr[3]), 4, 0 }, + { PT_GR(4) * 4, 4, greg_offset_and_size (gr[4]), 4, 0 }, + { PT_GR(5) * 4, 4, greg_offset_and_size (gr[5]), 4, 0 }, + { PT_GR(6) * 4, 4, greg_offset_and_size (gr[6]), 4, 0 }, + { PT_GR(7) * 4, 4, greg_offset_and_size (gr[7]), 4, 0 }, + { PT_GR(8) * 4, 4, greg_offset_and_size (gr[8]), 4, 0 }, + { PT_GR(9) * 4, 4, greg_offset_and_size (gr[9]), 4, 0 }, + { PT_GR(10) * 4, 4, greg_offset_and_size (gr[10]), 4, 0 }, + { PT_GR(11) * 4, 4, greg_offset_and_size (gr[11]), 4, 0 }, + { PT_GR(12) * 4, 4, greg_offset_and_size (gr[12]), 4, 0 }, + { PT_GR(13) * 4, 4, greg_offset_and_size (gr[13]), 4, 0 }, + { PT_GR(14) * 4, 4, greg_offset_and_size (gr[14]), 4, 0 }, + { PT_GR(15) * 4, 4, greg_offset_and_size (gr[15]), 4, 0 }, + { PT_GR(16) * 4, 4, greg_offset_and_size (gr[16]), 4, 0 }, + { PT_GR(17) * 4, 4, greg_offset_and_size (gr[17]), 4, 0 }, + { PT_GR(18) * 4, 4, greg_offset_and_size (gr[18]), 4, 0 }, + { PT_GR(19) * 4, 4, greg_offset_and_size (gr[19]), 4, 0 }, + { PT_GR(20) * 4, 4, greg_offset_and_size (gr[20]), 4, 0 }, + { PT_GR(21) * 4, 4, greg_offset_and_size (gr[21]), 4, 0 }, + { PT_GR(22) * 4, 4, greg_offset_and_size (gr[22]), 4, 0 }, + { PT_GR(23) * 4, 4, greg_offset_and_size (gr[23]), 4, 0 }, + { PT_GR(24) * 4, 4, greg_offset_and_size (gr[24]), 4, 0 }, + { PT_GR(25) * 4, 4, greg_offset_and_size (gr[25]), 4, 0 }, + { PT_GR(26) * 4, 4, greg_offset_and_size (gr[26]), 4, 0 }, + { PT_GR(27) * 4, 4, greg_offset_and_size (gr[27]), 4, 0 }, + { PT_GR(28) * 4, 4, greg_offset_and_size (gr[28]), 4, 0 }, + { PT_GR(29) * 4, 4, greg_offset_and_size (gr[29]), 4, 0 }, + { PT_GR(30) * 4, 4, greg_offset_and_size (gr[30]), 4, 0 }, + { PT_GR(31) * 4, 4, greg_offset_and_size (gr[31]), 4, 0 }, + { PT_GR(32) * 4, 4, greg_offset_and_size (gr[32]), 4, 0 }, + { PT_GR(33) * 4, 4, greg_offset_and_size (gr[33]), 4, 0 }, + { PT_GR(34) * 4, 4, greg_offset_and_size (gr[34]), 4, 0 }, + { PT_GR(35) * 4, 4, greg_offset_and_size (gr[35]), 4, 0 }, + { PT_GR(36) * 4, 4, greg_offset_and_size (gr[36]), 4, 0 }, + { PT_GR(37) * 4, 4, greg_offset_and_size (gr[37]), 4, 0 }, + { PT_GR(38) * 4, 4, greg_offset_and_size (gr[38]), 4, 0 }, + { PT_GR(39) * 4, 4, greg_offset_and_size (gr[39]), 4, 0 }, + { PT_GR(40) * 4, 4, greg_offset_and_size (gr[40]), 4, 0 }, + { PT_GR(41) * 4, 4, greg_offset_and_size (gr[41]), 4, 0 }, + { PT_GR(42) * 4, 4, greg_offset_and_size (gr[42]), 4, 0 }, + { PT_GR(43) * 4, 4, greg_offset_and_size (gr[43]), 4, 0 }, + { PT_GR(44) * 4, 4, greg_offset_and_size (gr[44]), 4, 0 }, + { PT_GR(45) * 4, 4, greg_offset_and_size (gr[45]), 4, 0 }, + { PT_GR(46) * 4, 4, greg_offset_and_size (gr[46]), 4, 0 }, + { PT_GR(47) * 4, 4, greg_offset_and_size (gr[47]), 4, 0 }, + { PT_GR(48) * 4, 4, greg_offset_and_size (gr[48]), 4, 0 }, + { PT_GR(49) * 4, 4, greg_offset_and_size (gr[49]), 4, 0 }, + { PT_GR(50) * 4, 4, greg_offset_and_size (gr[50]), 4, 0 }, + { PT_GR(51) * 4, 4, greg_offset_and_size (gr[51]), 4, 0 }, + { PT_GR(52) * 4, 4, greg_offset_and_size (gr[52]), 4, 0 }, + { PT_GR(53) * 4, 4, greg_offset_and_size (gr[53]), 4, 0 }, + { PT_GR(54) * 4, 4, greg_offset_and_size (gr[54]), 4, 0 }, + { PT_GR(55) * 4, 4, greg_offset_and_size (gr[55]), 4, 0 }, + { PT_GR(56) * 4, 4, greg_offset_and_size (gr[56]), 4, 0 }, + { PT_GR(57) * 4, 4, greg_offset_and_size (gr[57]), 4, 0 }, + { PT_GR(58) * 4, 4, greg_offset_and_size (gr[58]), 4, 0 }, + { PT_GR(59) * 4, 4, greg_offset_and_size (gr[59]), 4, 0 }, + { PT_GR(60) * 4, 4, greg_offset_and_size (gr[60]), 4, 0 }, + { PT_GR(61) * 4, 4, greg_offset_and_size (gr[61]), 4, 0 }, + { PT_GR(62) * 4, 4, greg_offset_and_size (gr[62]), 4, 0 }, + { PT_GR(63) * 4, 4, greg_offset_and_size (gr[63]), 4, 0 }, + + + { PT_FR(0) * 4, 4, fpreg_offset_and_size (fr[0]), 4, 0 }, + { PT_FR(1) * 4, 4, fpreg_offset_and_size (fr[1]), 4, 0 }, + { PT_FR(2) * 4, 4, fpreg_offset_and_size (fr[2]), 4, 0 }, + { PT_FR(3) * 4, 4, fpreg_offset_and_size (fr[3]), 4, 0 }, + { PT_FR(4) * 4, 4, fpreg_offset_and_size (fr[4]), 4, 0 }, + { PT_FR(5) * 4, 4, fpreg_offset_and_size (fr[5]), 4, 0 }, + { PT_FR(6) * 4, 4, fpreg_offset_and_size (fr[6]), 4, 0 }, + { PT_FR(7) * 4, 4, fpreg_offset_and_size (fr[7]), 4, 0 }, + { PT_FR(8) * 4, 4, fpreg_offset_and_size (fr[8]), 4, 0 }, + { PT_FR(9) * 4, 4, fpreg_offset_and_size (fr[9]), 4, 0 }, + { PT_FR(10) * 4, 4, fpreg_offset_and_size (fr[10]), 4, 0 }, + { PT_FR(11) * 4, 4, fpreg_offset_and_size (fr[11]), 4, 0 }, + { PT_FR(12) * 4, 4, fpreg_offset_and_size (fr[12]), 4, 0 }, + { PT_FR(13) * 4, 4, fpreg_offset_and_size (fr[13]), 4, 0 }, + { PT_FR(14) * 4, 4, fpreg_offset_and_size (fr[14]), 4, 0 }, + { PT_FR(15) * 4, 4, fpreg_offset_and_size (fr[15]), 4, 0 }, + { PT_FR(16) * 4, 4, fpreg_offset_and_size (fr[16]), 4, 0 }, + { PT_FR(17) * 4, 4, fpreg_offset_and_size (fr[17]), 4, 0 }, + { PT_FR(18) * 4, 4, fpreg_offset_and_size (fr[18]), 4, 0 }, + { PT_FR(19) * 4, 4, fpreg_offset_and_size (fr[19]), 4, 0 }, + { PT_FR(20) * 4, 4, fpreg_offset_and_size (fr[20]), 4, 0 }, + { PT_FR(21) * 4, 4, fpreg_offset_and_size (fr[21]), 4, 0 }, + { PT_FR(22) * 4, 4, fpreg_offset_and_size (fr[22]), 4, 0 }, + { PT_FR(23) * 4, 4, fpreg_offset_and_size (fr[23]), 4, 0 }, + { PT_FR(24) * 4, 4, fpreg_offset_and_size (fr[24]), 4, 0 }, + { PT_FR(25) * 4, 4, fpreg_offset_and_size (fr[25]), 4, 0 }, + { PT_FR(26) * 4, 4, fpreg_offset_and_size (fr[26]), 4, 0 }, + { PT_FR(27) * 4, 4, fpreg_offset_and_size (fr[27]), 4, 0 }, + { PT_FR(28) * 4, 4, fpreg_offset_and_size (fr[28]), 4, 0 }, + { PT_FR(29) * 4, 4, fpreg_offset_and_size (fr[29]), 4, 0 }, + { PT_FR(30) * 4, 4, fpreg_offset_and_size (fr[30]), 4, 0 }, + { PT_FR(31) * 4, 4, fpreg_offset_and_size (fr[31]), 4, 0 }, + { PT_FR(32) * 4, 4, fpreg_offset_and_size (fr[32]), 4, 0 }, + { PT_FR(33) * 4, 4, fpreg_offset_and_size (fr[33]), 4, 0 }, + { PT_FR(34) * 4, 4, fpreg_offset_and_size (fr[34]), 4, 0 }, + { PT_FR(35) * 4, 4, fpreg_offset_and_size (fr[35]), 4, 0 }, + { PT_FR(36) * 4, 4, fpreg_offset_and_size (fr[36]), 4, 0 }, + { PT_FR(37) * 4, 4, fpreg_offset_and_size (fr[37]), 4, 0 }, + { PT_FR(38) * 4, 4, fpreg_offset_and_size (fr[38]), 4, 0 }, + { PT_FR(39) * 4, 4, fpreg_offset_and_size (fr[39]), 4, 0 }, + { PT_FR(40) * 4, 4, fpreg_offset_and_size (fr[40]), 4, 0 }, + { PT_FR(41) * 4, 4, fpreg_offset_and_size (fr[41]), 4, 0 }, + { PT_FR(42) * 4, 4, fpreg_offset_and_size (fr[42]), 4, 0 }, + { PT_FR(43) * 4, 4, fpreg_offset_and_size (fr[43]), 4, 0 }, + { PT_FR(44) * 4, 4, fpreg_offset_and_size (fr[44]), 4, 0 }, + { PT_FR(45) * 4, 4, fpreg_offset_and_size (fr[45]), 4, 0 }, + { PT_FR(46) * 4, 4, fpreg_offset_and_size (fr[46]), 4, 0 }, + { PT_FR(47) * 4, 4, fpreg_offset_and_size (fr[47]), 4, 0 }, + { PT_FR(48) * 4, 4, fpreg_offset_and_size (fr[48]), 4, 0 }, + { PT_FR(49) * 4, 4, fpreg_offset_and_size (fr[49]), 4, 0 }, + { PT_FR(50) * 4, 4, fpreg_offset_and_size (fr[50]), 4, 0 }, + { PT_FR(51) * 4, 4, fpreg_offset_and_size (fr[51]), 4, 0 }, + { PT_FR(52) * 4, 4, fpreg_offset_and_size (fr[52]), 4, 0 }, + { PT_FR(53) * 4, 4, fpreg_offset_and_size (fr[53]), 4, 0 }, + { PT_FR(54) * 4, 4, fpreg_offset_and_size (fr[54]), 4, 0 }, + { PT_FR(55) * 4, 4, fpreg_offset_and_size (fr[55]), 4, 0 }, + { PT_FR(56) * 4, 4, fpreg_offset_and_size (fr[56]), 4, 0 }, + { PT_FR(57) * 4, 4, fpreg_offset_and_size (fr[57]), 4, 0 }, + { PT_FR(58) * 4, 4, fpreg_offset_and_size (fr[58]), 4, 0 }, + { PT_FR(59) * 4, 4, fpreg_offset_and_size (fr[59]), 4, 0 }, + { PT_FR(60) * 4, 4, fpreg_offset_and_size (fr[60]), 4, 0 }, + { PT_FR(61) * 4, 4, fpreg_offset_and_size (fr[61]), 4, 0 }, + { PT_FR(62) * 4, 4, fpreg_offset_and_size (fr[62]), 4, 0 }, + { PT_FR(63) * 4, 4, fpreg_offset_and_size (fr[63]), 4, 0 }, + + { PT_PC * 4, 4, greg_offset_and_size (pc), 4, 0 }, + { PT_PSR * 4, 4, greg_offset_and_size (psr), 4, 0 }, + { PT_CCR * 4, 4, greg_offset_and_size (ccr), 4, 0 }, + { PT_CCCR * 4, 4, greg_offset_and_size (cccr), 4, 0 }, + + /* FDPIC "fake" registers for obtaining loadmap addresses: + FDPIC_INTERP and FDPIC_EXEC... */ +#define PTRACE_GETFDPIC 31 +#define PTRACE_GETFDPIC_EXEC 0 +#define PTRACE_GETFDPIC_INTERP 1 + { PTRACE_GETFDPIC_EXEC, 4, OTHERREGS, 0, 0, 4, frv_fdpic_loadmap_addresses }, + { PTRACE_GETFDPIC_INTERP, 4, OTHERREGS, 0, 0, 4, frv_fdpic_loadmap_addresses }, + + /* 134 is unspecified. */ + { 0, 0, noreg_offset_and_size (134), 4, 0 }, + + /* tbr */ + { 0, 0, noreg_offset_and_size (135), 4, 0 }, + + /* brr */ + { 0, 0, noreg_offset_and_size (136), 4, 0 }, + + /* dbar0 - dbar3 */ + { 0, 0, noreg_offset_and_size (137), 4, 0 }, + { 0, 0, noreg_offset_and_size (138), 4, 0 }, + { 0, 0, noreg_offset_and_size (139), 4, 0 }, + { 0, 0, noreg_offset_and_size (140), 4, 0 }, + + /* 141 - 144 are unspecified. */ + { 0, 0, noreg_offset_and_size (141), 4, 0 }, + { 0, 0, noreg_offset_and_size (142), 4, 0 }, + { 0, 0, noreg_offset_and_size (143), 4, 0 }, + { 0, 0, noreg_offset_and_size (144), 4, 0 }, + + { PT_LR * 4, 4, greg_offset_and_size (lr), 4, 0 }, + { PT_LCR * 4, 4, greg_offset_and_size (lcr), 4, 0 }, + + /* Can't use greg_offset_and_size for iacc0h and iacc0l because the iacc + field is 64-bits wide. We need to provide access to the individual + 32-bit halves. */ + { PT_IACC0H * 4, 4, GREGS, offsetof (struct user_int_regs, iacc[0]), 4, 4, 0 }, + { PT_IACC0L * 4, 4, GREGS, offsetof (struct user_int_regs, iacc[0]) + 4, 4, 4, 0 }, + { PT_FSR(0) * 4, 4, fpreg_offset_and_size (fsr[0]), 4, 0 }, + { PT_ACC(0) * 4, 4, fpreg_offset_and_size (acc[0]), 4, 0 }, + { PT_ACC(1) * 4, 4, fpreg_offset_and_size (acc[1]), 4, 0 }, + { PT_ACC(2) * 4, 4, fpreg_offset_and_size (acc[2]), 4, 0 }, + { PT_ACC(3) * 4, 4, fpreg_offset_and_size (acc[3]), 4, 0 }, + { PT_ACC(4) * 4, 4, fpreg_offset_and_size (acc[4]), 4, 0 }, + { PT_ACC(5) * 4, 4, fpreg_offset_and_size (acc[5]), 4, 0 }, + { PT_ACC(6) * 4, 4, fpreg_offset_and_size (acc[6]), 4, 0 }, + { PT_ACC(7) * 4, 4, fpreg_offset_and_size (acc[7]), 4, 0 }, + + /* For the one-byte ACCG regs, ptrace() fetches four regs at a time, + but the user_fpmedia_regs struct breaks the regs out into an array + of bytes. Thus, we can't use the fpreg_offset_and_size macro. */ + { PT_ACCG(0) * 4, 4, FPREGS, offsetof (struct user_fpmedia_regs, acc[0]), 4, 4, 0 }, + { PT_ACCG(1) * 4, 4, FPREGS, offsetof (struct user_fpmedia_regs, acc[4]), 4, 4, 0 }, + + { PT_MSR(0) * 4, 4, fpreg_offset_and_size (msr[0]), 4, 0 }, + { PT_MSR(1) * 4, 4, fpreg_offset_and_size (msr[0]), 4, 0 }, + + { PT_GNER0 * 4, 4, greg_offset_and_size (gner[0]), 4, 0 }, + { PT_GNER1 * 4, 4, greg_offset_and_size (gner[1]), 4, 0 }, + + { PT_FNER(0) * 4, 4, fpreg_offset_and_size (fner[0]), 4, 0 }, + { PT_FNER(1) * 4, 4, fpreg_offset_and_size (fner[1]), 4, 0 }, +}; + +int +frv_fdpic_loadmap_addresses (struct gdbserv *serv, int pid, int regno, + void *read_buf, const void *write_buf) +{ + /* We can only read the load map addresses; writing them is not supported. */ + if (read_buf != NULL) + { + unsigned long val; + long status; + struct child_process *process = gdbserv_target_data (serv); + + status = ptrace (PTRACE_GETFDPIC, pid, + (void *)reginfo[regno].ptrace_offset, + &val); + if (process->debug_backend) + fprintf (stderr, "PTRACE_GETFDPIC pid=%d offset=%d val=%x\n", + pid,reginfo[regno].ptrace_offset,val); + if (status < 0) + return errno; + else + { + memcpy (read_buf, &val, sizeof val); + return 0; + } + } + return 0; +} + +/* End of FRV_LINUX_TARGET */ +#else +#error Need a _LINUX_TARGET define for your architecture +#endif + +/* The following functions should work either PEEKUSER_POKEUSER_REGINFO, + GETREGS_SETREGS_REGINFO, or the old mechanism. */ + +/* Function: next_gg_reg + This generic version useful only for targets whose + register numbers are the same as their position in the g/G packet. + Return next register number or -1 for end. */ + +static int +linux_next_gg_reg (struct gdbserv *serv, + struct gdbserv_thread *thread, + int lastreg) +{ + /* This function is an iterator. + If called with -1, it returns the first register number. + Else it returns the next register number until there are no more, + whereupon it returns -1. */ + + if (lastreg < 0) + return 0; + else if (lastreg >= NUM_REGS - 1) + return -1; + else + return lastreg + 1; +} + +/* Function: gg_reg_nr + This generic version useful only for targets whose + register numbers are the same as their position in the g/G packet. + Return register number or -1 for a bad index. */ + +static int +linux_gg_reg_nr (struct gdbserv *serv, int index) +{ + /* This function returns the register number of the "i'th" register + in the g/G packet. In the default case that's just i. */ + + if (index >= 0 && index < NUM_REGS) + return index; + else + return -1; +} + +/* Function: get_xregsetsize + Fetch the size of the extended register set. */ + +static int +get_xregsetsize (struct gdbserv *serv, int pid) +{ +#if defined (X86_LINUX_TARGET) + return sizeof (elf_fpxregset_t); +#else + return -1; +#endif +} + + +#if defined (PEEKUSER_POKEUSER_REGINFO) || defined (GETREGS_SETREGS_REGINFO) +/* The following functions will only work with the PEEKUSER_POKEUSER_REGINFO + and GETREGS_SETREGS_REGINFO mechanisms. */ + +/* Function: reg_format + This generic version useful only for targets whose + registers are all the same size. + Return zero for success, -1 for failure (never fails). */ + +static int +linux_reg_format (struct gdbserv *serv, + struct gdbserv_thread *thread, + int regno, int *size, int *padding) +{ + /* Size and formatting (padding) of thread in g/G packet. */ + *size = reginfo[regno].proto_size; + *padding = 0; + return 0; +} + +/* Function: sizeof_reg + This generic version useful only for targets whose + registers are all the same size. + Returns size of a register. */ + +static long linux_sizeof_reg (struct gdbserv *serv, int regno) +{ + return reginfo[regno].proto_size; +} + +/* Return 1 if REGNUM represents a general purpose register which has + space allocated for it in GREGSET_T. Otherwise, return 0. */ + +int +is_gp_reg (int regnum) +{ + if (regnum >= 0 && regnum < NUM_REGS) + return reginfo[regnum].whichregs == GREGS; + else + return 0; +} + +/* Return 1 if REGNUM represents a floating point register, 0 otherwise. + For these purposes, floating point status registers (and perhaps certain + other registers) are considered to be floating point registers when + they appear in the fpregset_t struct. */ + +int +is_fp_reg (int regnum) +{ + if (regnum >= 0 && regnum < NUM_REGS) + return reginfo[regnum].whichregs == FPREGS; + else + return 0; +} + +/* Return 1 if REGNUM represents a register in the extended register + set, 0 otherwise. The definition of what consitutes an extended + register is implementation specific. For x86 though, an extended + register is one of the extended floating point registers. */ + +int +is_extended_reg (int regnum) +{ + if (regnum >= 0 && regnum < NUM_REGS) + return reginfo[regnum].whichregs == FPXREGS; + else + return 0; +} + +#endif /* defined (PEEKUSER_POKEUSER_REGINFO) || defined (GETREGS_SETREGS_REGINFO) */ + +#if defined (PEEKUSER_POKEUSER_REGINFO) + +/* Functions which implement the PEEKUSER_POKEUSER_REGINFO machinery... */ + +/* Fetch the register indicated by REGNO into the buffer REG_BYTES. + The caller must ensure that a sufficiently large buffer has been + allocated. + Returns 0 for success, -1 for failure. */ + +static int +read_reg_bytes (struct gdbserv *serv, int pid, int regno, void *reg_bytes) +{ + ptrace_arg3_type regaddr; + int regsize; + int status; + + if (regno < 0 || regno >= NUM_REGS) + return -1; + + if (reginfo[regno].alternate_register_read_write_method == NULL) + { + regaddr = reginfo[regno].ptrace_offset; + regsize = reginfo[regno].ptrace_size; + status = ptrace_read_user (serv, pid, regaddr, regsize, reg_bytes); + /* A non-zero status is the errno value from the ptrace call. */ + if (status != 0) + { + fprintf (stderr, "Error: PT_READ_U at 0x%08lx in process %d\n", + (long) regaddr, pid); + return -1; + } + } + else + { + /* Use alternate reader. */ + status = reginfo[regno].alternate_register_read_write_method + (serv, pid, regno, reg_bytes, 0); + if (status != 0) + { + fprintf (stderr, + "read_reg_bytes: Error: Couldn't read register using alternate method, regno=%d, status=%d\n", + regno, status); + return -1; + } + } + + return 0; +} + +/* Store the buffer REG_BYTES to the register indicated by REGNO. + Returns 0 for success, -1 for failure. */ + +static int +write_reg_bytes (struct gdbserv *serv, int pid, int regno, + const void *reg_bytes) +{ + ptrace_arg3_type regaddr; + int regsize; + int status; + + if (regno < 0 || regno >= NUM_REGS) + return -1; + + if (reginfo[regno].alternate_register_read_write_method == NULL) + { + regaddr = reginfo[regno].ptrace_offset; + regsize = reginfo[regno].ptrace_size; + status = ptrace_write_user (serv, pid, regaddr, regsize, reg_bytes); + + /* A non-zero status is the errno value from the ptrace call */ + if (status != 0) + { + fprintf (stderr, "Error: PT_WRITE_U status=%d at 0x%08lx in process %d\n", + status, (long) regaddr, pid); + return -1; + } + } + else + { + /* Use alternate writer. */ + status = reginfo[regno].alternate_register_read_write_method + (serv, pid, regno, 0, reg_bytes); + if (status != 0) + { + fprintf (stderr, + "write_reg_bytes: Error: Couldn't write register using alternate method, regno=%d, status=%d", + regno, status); + return -1; + } + } + return 0; +} + + +/* Fetch and return the value of register REGNO. Helper function for + debug_get_pc(). This is the PEEKUSER_POKEUSER_REGINFO version. */ +static unsigned long +debug_get_reg (struct gdbserv *serv, pid_t pid, int regno) +{ + ptrace_xfer_type value; + + if (read_reg_bytes (serv, pid, regno, &value) < 0) + return 0; + else + return (unsigned long) value; +} + +/* Fetch and return the value of the PC. Needed by thread-db.c. */ +unsigned long +debug_get_pc (struct gdbserv *serv, pid_t pid) +{ + return debug_get_reg (serv, pid, PC_REGNUM); +} + +/* Function: get_reg + This version is for targets for which all registers may + be fetched using PTRACE_PEEKUSER. + Return -1 for failure, zero for success. */ + +static int +linux_get_reg (struct gdbserv *serv, int regno, struct gdbserv_reg *reg) +{ + struct child_process *process = gdbserv_target_data (serv); + char tmp_buf[MAX_REG_SIZE]; + int status; + + if (regno < 0 || regno >= NUM_REGS) + { + fprintf (stderr, "Error: linux_get_reg: Register %d out of bounds.\n", regno); + return -1; + } + + if (reginfo[regno].whichregs != NOREGS) + { + /* Get the register value. */ + status = read_reg_bytes (serv, process->pid, regno, tmp_buf); + if (status < 0) + return -1; /* fail */ + } + else + memset (tmp_buf, 0, reginfo[regno].ptrace_size); + + /* Copy the bytes to the gdbserv_reg struct. */ + gdbserv_host_bytes_to_reg (serv, tmp_buf, reginfo[regno].ptrace_size, + reg, reginfo[regno].proto_size, sign_extend); + + return 0; /* success */ + +} + +/* Function: set_reg + This version is for targets which are capable of setting any register + via PTRACE_POKEUSER. + Return -1 for failure, zero for success. */ + +static int +linux_set_reg (struct gdbserv *serv, int regno, struct gdbserv_reg *reg) +{ + if (regno < 0 || regno >= NUM_REGS) + { + fprintf (stderr, "Error: linux_set_reg: Register %d out of bounds.\n", regno); + return -1; + } + + if (reginfo[regno].whichregs != NOREGS) + { + struct child_process *process = gdbserv_target_data (serv); + char tmp_buf[MAX_REG_SIZE]; + int status; + + /* Copy the bytes from the gdbserv_reg struct to our temporary buffer. */ + gdbserv_host_bytes_from_reg (serv, tmp_buf, reginfo[regno].ptrace_size, + reg, sign_extend); + + /* Write the child's register. */ + status = write_reg_bytes (serv, process->pid, regno, tmp_buf); + if (status < 0) + return -1; /* Fail */ + } + + return 0; /* success */ +} + +/* gregset functions */ + +/* Helper function for reg_from_gregset / reg_from_fpregset */ + +int +reg_from_regset (struct gdbserv *serv, + struct gdbserv_reg *reg, + int regno, + const void *regset, + enum regset whichregs) +{ + char *regbytes; + + if (regno < 0 || regno >= NUM_REGS + || reginfo[regno].whichregs != whichregs) + { + return -1; + } + + regbytes = ((char *) regset) + reginfo[regno].regset_field_offset; + + gdbserv_host_bytes_to_reg (serv, regbytes, reginfo[regno].regset_field_size, + reg, reginfo[regno].proto_size, sign_extend); + + return 0; +} + +/* Helper function for reg_to_gregset / reg_to_fpregset. + Insert register into the regset indicated by WHICHREGS by REGNO. + Return -1 for failure, zero for success. */ + +int +reg_to_regset (struct gdbserv *serv, + const struct gdbserv_reg *reg, + int regno, + void *regset, + enum regset whichregs) +{ + char *regbytes; + + if (regno < 0 || regno >= NUM_REGS + || reginfo[regno].whichregs != whichregs) + { + return -1; + } + + regbytes = ((char *) regset) + reginfo[regno].regset_field_offset; + + gdbserv_host_bytes_from_reg (serv, regbytes, reginfo[regno].regset_field_size, + reg, sign_extend); + + return 0; +} + +/* Function: reg_from_gregset + Extract register from gregset by regnum. + Return -1 for failure, zero for success. */ + +int +reg_from_gregset (struct gdbserv *serv, + struct gdbserv_reg *reg, + int regno, + const GREGSET_T gregset) +{ + return reg_from_regset (serv, reg, regno, gregset, GREGS); +} + +/* Function: reg_to_gregset + Insert register into gregset by regnum. + Return -1 for failure, zero for success. */ + +int +reg_to_gregset (struct gdbserv *serv, + const struct gdbserv_reg *reg, + int regno, + GREGSET_T gregset) +{ + return reg_to_regset (serv, reg, regno, gregset, GREGS); +} + +/* Function: reg_from_fpregset + Extract register from fpregset by regnum. + Return -1 for failure, zero for success. */ + +int +reg_from_fpregset (struct gdbserv *serv, + struct gdbserv_reg *reg, + int regno, + const FPREGSET_T *fpregset) +{ + return reg_from_regset (serv, reg, regno, fpregset, FPREGS); +} + +/* Function: reg_to_fpregset + Insert register into fpregset by regnum. + Return -1 for failure, zero for success. */ + +int +reg_to_fpregset (struct gdbserv *serv, + const struct gdbserv_reg *reg, + int regno, + FPREGSET_T *fpregset) +{ + return reg_to_regset (serv, reg, regno, fpregset, FPREGS); +} + +/* Function: reg_from_xregset + Extract register from extended regset by regnum. + Return -1 for failure, zero for success. */ + +int +reg_from_xregset (struct gdbserv *serv, + struct gdbserv_reg *reg, + int regno, + const void *xregset) +{ + return -1; /* Unimplemented. */ +} + +/* Function: reg_to_xregset + Insert register into extended regset by regnum. + Return -1 for failure, zero for success. */ + +int +reg_to_xregset (struct gdbserv *serv, + const struct gdbserv_reg *reg, + int regno, + void *xregset) +{ + return -1; /* Unimplemented. */ +} + +/* Helper function for get_gregset / get_fpregset */ + +static int +get_regset (struct gdbserv *serv, int pid, void *regset, + enum regset whichregs) +{ + int regno; + + for (regno = 0; regno < NUM_REGS; regno++) + { + if (reginfo[regno].whichregs == whichregs) + { + char tmp_buf[MAX_REG_SIZE]; + struct gdbserv_reg reg; + int status; + + /* Get the register value. */ + status = read_reg_bytes (serv, pid, regno, tmp_buf); + if (status < 0) + return -1; /* fail */ + + /* Copy the bytes to the gdbserv_reg struct. */ + gdbserv_host_bytes_to_reg (serv, tmp_buf, reginfo[regno].ptrace_size, + ®, reginfo[regno].proto_size, + sign_extend); + + /* Now insert them into the regset. */ + reg_to_regset (serv, ®, regno, regset, whichregs); + } + } + + return 0; +} +/* Helper function for put_gregset / put_fpregset. + Write the regset indicated by WHICHREGS for PID. + Return -1 for failure, zero for success. */ + +static int +put_regset (struct gdbserv *serv, + int pid, + const void *regset, + enum regset whichregs) +{ + int regno; + + for (regno = 0; regno < NUM_REGS; regno++) + { + if (reginfo[regno].whichregs == whichregs) + { + char tmp_buf[MAX_REG_SIZE]; + struct gdbserv_reg reg; + int status; + + /* Fetch the reg from the regset. */ + reg_from_regset (serv, ®, regno, regset, whichregs); + + /* Copy the bytes from the gdbserv_reg struct to our temporary buffer. */ + gdbserv_host_bytes_from_reg (serv, tmp_buf, + reginfo[regno].ptrace_size, ®, + sign_extend); + + /* Write the child's register. */ + status = write_reg_bytes (serv, pid, regno, tmp_buf); + if (status < 0) + return -1; /* Fail */ + } + } + return 0; +} + +/* Function: get_gregset + Read the gregset for pid. + Return -1 for failure, zero for success. */ + +static int +get_gregset (struct gdbserv *serv, int pid, GREGSET_T gregset) +{ + return get_regset (serv, pid, gregset, GREGS); +} + +/* Function: put_gregset + write the gregset for pid. + Return -1 for failure, zero for success. */ + +static int +put_gregset (struct gdbserv *serv, int pid, const GREGSET_T gregset) +{ + return put_regset (serv, pid, gregset, GREGS); +} + +static int +get_fpregset (struct gdbserv *serv, int pid, FPREGSET_T *fpregset) +{ + return get_regset (serv, pid, fpregset, FPREGS); +} + +/* Function: put_fpregset + write the gregset for pid. + Return -1 for failure, zero for success. */ + +static int +put_fpregset (struct gdbserv *serv, int pid, const FPREGSET_T *fpregset) +{ + return put_regset (serv, pid, fpregset, FPREGS); +} + +/* Function: get_fpregset + Read the fpregset for pid. + Return -1 for failure, zero for success. */ + +static int +get_xregset (struct gdbserv *serv, int pid, void *fpregset) +{ + return -1; +} + +/* Function: put_xregset + write the gregset for pid. + Return -1 for failure, zero for success. */ + +static int +put_xregset (struct gdbserv *serv, int pid, const void *fpregset) +{ + return -1; +} + +#elif defined (GETREGS_SETREGS_REGINFO) + +/* Functions which implement the GETREGS_SETREGS_REGINFO machinery... */ + +/* Forward declarations for get_gregset() and put_gregset(). */ +static int get_gregset (struct gdbserv *serv, int pid, GREGSET_T gregset); +static int put_gregset (struct gdbserv *serv, int pid, const GREGSET_T gregset); + +/* Fetch register REGNO as an unsigned long and return it via the REGVAL + pointer. Return 0 for success and -1 for failure. */ +static int +read_reg_as_ulong (struct gdbserv *serv, pid_t pid, int regno, + unsigned long *regval) +{ + struct gdbserv_reg reg; + GREGSET_T gregset; + int status; + + status = get_gregset (serv, pid, gregset); + if (status < 0) + return status; + + status = reg_from_gregset (serv, ®, regno, gregset); + if (status < 0) + return status; + + gdbserv_reg_to_ulong (serv, ®, regval); + return status; +} + +/* Write the unsigned long value REGVAL to register REGNO. Return 0 + for success and -1 for failure. */ +static int +write_reg_as_ulong (struct gdbserv *serv, pid_t pid, int regno, + unsigned long regval) +{ + struct gdbserv_reg reg; + GREGSET_T gregset; + int status; + + status = get_gregset (serv, pid, gregset); + if (status < 0) + return status; + + gdbserv_ulong_to_reg (serv, regval, ®); + + status = reg_to_gregset (serv, ®, regno, gregset); + if (status < 0) + return status; + + status = put_gregset (serv, pid, gregset); + + return status; +} + +/* Fetch and return the value of register REGNO. Helper function for + debug_get_pc(). This is the GETREGS_SETREGS_REGINFO version. */ +static unsigned long +debug_get_reg (struct gdbserv *serv, pid_t pid, int regno) +{ + int status; + unsigned long regval; + + status = read_reg_as_ulong (serv, pid, regno, ®val); + if (status < 0) + return 0; + else + return regval; +} + +/* Fetch and return the value of the PC. Needed by thread-db.c. */ +unsigned long +debug_get_pc (struct gdbserv *serv, pid_t pid) +{ + return debug_get_reg (serv, pid, PC_REGNUM); +} + +/* Function: get_reg + This version is for targets which need to fetch registers + en masse via ptrace(). + Return -1 for failure, zero for success. */ + +static int +linux_get_reg (struct gdbserv *serv, int regno, struct gdbserv_reg *reg) +{ + int ret; + elf_gregset_t gregs; + elf_fpregset_t fpregs; + void *fpxregs; + char *buf; + + if (regno < 0 || regno >= NUM_REGS) + { + fprintf (stderr, "Error: linux_get_reg: Register %d out of bounds.\n", regno); + return -1; + } + + /* Fetch the appropriate register set. */ + if (reginfo[regno].whichregs == GREGS) + { + ret = ptrace_get_gregs (serv, 0, &gregs); + if (ret != 0) + { + /* FIXME: Do we need a perror() here? */ + return -1; + } + buf = (char *) &gregs; + } + else if (reginfo[regno].whichregs == FPREGS) + { + ret = ptrace_get_fpregs (serv, 0, &fpregs); + if (ret != 0) + return -1; + buf = (char *) &fpregs; + } + else if (reginfo[regno].whichregs == FPXREGS) + { + struct child_process *process = gdbserv_target_data (serv); + int xregsize = get_xregsetsize (serv, process->pid); + + assert (xregsize >= 0); + + fpxregs = alloca (xregsize); + ret = ptrace_get_fpxregs (serv, 0, fpxregs); + if (ret != 0) + return -1; + buf = (char *) fpxregs; + } + else if (reginfo[regno].whichregs == NOREGS) + { + /* Do nothing. */ + } + else + { + return -1; + } + + /* Adjust buf to point at the starting byte of the register. */ + buf += reginfo[regno].offset; + + /* Copy the bytes to the gdbserv_reg struct. */ + gdbserv_host_bytes_to_reg (serv, buf, reginfo[regno].ptrace_size, + reg, reginfo[regno].proto_size, sign_extend); + + return 0; +} + +/* Function: linux_set_reg + This version is for targets which need to set registers en masse. + Return -1 for failure, zero for success. */ + +static int +linux_set_reg (struct gdbserv *serv, int regno, struct gdbserv_reg *reg) +{ + int ret; + elf_gregset_t gregs; + elf_fpregset_t fpregs; + void *fpxregs = NULL; + char *buf; + char tmp_buf[MAX_REG_SIZE]; + + if (regno < 0 || regno >= NUM_REGS) + { + fprintf (stderr, "Error: linux_set_reg: Register %d out of bounds.\n", regno); + return -1; + } + + /* Fetch the appropriate register set. */ + if (reginfo[regno].whichregs == GREGS) + { + ret = ptrace_get_gregs (serv, 0, &gregs); + if (ret != 0) + return ret; + buf = (char *) &gregs; + } + else if (reginfo[regno].whichregs == FPREGS) + { + ret = ptrace_get_fpregs (serv, 0, &fpregs); + if (ret != 0) + return ret; + buf = (char *) &fpregs; + } + else if (reginfo[regno].whichregs == FPXREGS) + { + struct child_process *process = gdbserv_target_data (serv); + int xregsize = get_xregsetsize (serv, process->pid); + + assert (xregsize >= 0); + + fpxregs = alloca (xregsize); + ret = ptrace_get_fpxregs (serv, 0, fpxregs); + if (ret != 0) + return ret; + buf = (char *) fpxregs; + } + else if (reginfo[regno].whichregs == NOREGS) + { + /* Nothing to do, return immediately. */ + return 0; + } + else + { + return -1; + } + + /* Adjust buf to point at the starting byte of the register. */ + buf += reginfo[regno].offset; + + /* Copy the bytes from the gdbserv_reg struct to our temporary buffer. */ + gdbserv_host_bytes_from_reg (serv, buf, reginfo[regno].ptrace_size, reg, + sign_extend); + + /* Write the register set to the process. */ + if (reginfo[regno].whichregs == GREGS) + { + ret = ptrace_set_gregs (serv, 0, &gregs); + if (ret != 0) + return ret; + } + else if (reginfo[regno].whichregs == FPREGS) + { + ret = ptrace_set_fpregs (serv, 0, &fpregs); + if (ret != 0) + return ret; + } + else if (reginfo[regno].whichregs == FPXREGS) + { + ret = ptrace_set_fpxregs (serv, 0, &fpxregs); + if (ret != 0) + return ret; + } + else + { + /* Can't happen. */ + return -1; + } + + return 0; +} + +/* gregset functions */ + +/* Function: reg_from_gregset + Extract register from gregset by regnum. + Return -1 for failure, zero for success. */ + +int +reg_from_gregset (struct gdbserv *serv, + struct gdbserv_reg *reg, + int regno, + const GREGSET_T gregset) +{ + char *regbytes; + + if (regno < 0 || regno >= NUM_REGS + || reginfo[regno].whichregs != GREGS) + { + return -1; + } + + regbytes = ((char *) gregset) + reginfo[regno].offset; + + gdbserv_host_bytes_to_reg (serv, regbytes, reginfo[regno].ptrace_size, + reg, reginfo[regno].proto_size, sign_extend); + + return 0; +} + +/* Function: reg_to_gregset + Insert register into gregset by regnum. + Return -1 for failure, zero for success. */ + +int +reg_to_gregset (struct gdbserv *serv, + const struct gdbserv_reg *reg, + int regno, + GREGSET_T gregset) +{ + char *regbytes; + + if (regno < 0 || regno >= NUM_REGS + || reginfo[regno].whichregs != GREGS) + { + return -1; + } + + regbytes = ((char *) gregset) + reginfo[regno].offset; + + gdbserv_host_bytes_from_reg (serv, regbytes, reginfo[regno].ptrace_size, reg, + sign_extend); + + return 0; +} + +/* Function: reg_from_fpregset + Extract register from fpregset by regnum. + Return -1 for failure, zero for success. */ + +int +reg_from_fpregset (struct gdbserv *serv, + struct gdbserv_reg *reg, + int regno, + const FPREGSET_T *fpregset) +{ + char *regbytes; + + if (regno < 0 || regno >= NUM_REGS + || reginfo[regno].whichregs != FPREGS) + { + return -1; + } + + regbytes = ((char *) fpregset) + reginfo[regno].offset; + + gdbserv_host_bytes_to_reg (serv, regbytes, reginfo[regno].ptrace_size, + reg, reginfo[regno].proto_size, sign_extend); + + return 0; +} + +/* Function: reg_to_fpregset + Insert register into fpregset by regnum. + Return -1 for failure, zero for success. */ + +int +reg_to_fpregset (struct gdbserv *serv, + const struct gdbserv_reg *reg, + int regno, + FPREGSET_T *fpregset) +{ + char *regbytes; + + if (regno < 0 || regno >= NUM_REGS + || reginfo[regno].whichregs != FPREGS) + { + return -1; + } + + regbytes = ((char *) fpregset) + reginfo[regno].offset; + + gdbserv_host_bytes_from_reg (serv, regbytes, reginfo[regno].ptrace_size, reg, + sign_extend); + + return 0; +} + +/* Function: reg_from_xregset + Extract register from extended regset by regnum. + Return -1 for failure, zero for success. */ + +int +reg_from_xregset (struct gdbserv *serv, + struct gdbserv_reg *reg, + int regno, + const void *xregset) +{ + char *regbytes; + + if (regno < 0 || regno >= NUM_REGS + || reginfo[regno].whichregs != FPXREGS) + { + return -1; + } + + regbytes = ((char *) xregset) + reginfo[regno].offset; + + gdbserv_host_bytes_to_reg (serv, regbytes, reginfo[regno].ptrace_size, + reg, reginfo[regno].proto_size, sign_extend); + + return 0; +} + +/* Function: reg_to_xregset + Insert register into extended regset by regnum. + Return -1 for failure, zero for success. */ + +int +reg_to_xregset (struct gdbserv *serv, + const struct gdbserv_reg *reg, + int regno, + void *xregset) +{ + char *regbytes; + + if (regno < 0 || regno >= NUM_REGS + || reginfo[regno].whichregs != FPXREGS) + { + return -1; + } + + regbytes = ((char *) xregset) + reginfo[regno].offset; + + gdbserv_host_bytes_from_reg (serv, regbytes, reginfo[regno].ptrace_size, reg, + sign_extend); + + return 0; +} + +/* Function: get_gregset + Read the gregset for pid. + Return -1 for failure, zero for success. */ + +static int +get_gregset (struct gdbserv *serv, int pid, GREGSET_T gregset) +{ + int status; + + status = ptrace_get_gregs (serv, pid, gregset); + return status; +} + +/* Function: put_gregset + write the gregset for pid. + Return -1 for failure, zero for success. */ + +static int +put_gregset (struct gdbserv *serv, int pid, const GREGSET_T gregset) +{ + int status; + + status = ptrace_set_gregs (serv, pid, gregset); + + return status; +} + +/* Function: get_fpregset + Read the fpregset for pid. + Return -1 for failure, zero for success. */ + +static int +get_fpregset (struct gdbserv *serv, int pid, FPREGSET_T *fpregset) +{ + int status; + + status = ptrace_get_fpregs (serv, pid, fpregset); + return status; +} + +/* Function: put_fpregset + write the gregset for pid. + Return -1 for failure, zero for success. */ + +static int +put_fpregset (struct gdbserv *serv, int pid, const FPREGSET_T *fpregset) +{ + int status; + + status = ptrace_set_fpregs (serv, pid, fpregset); + + return status; +} + +/* Function: get_fpregset + Read the fpregset for pid. + Return -1 for failure, zero for success. */ + +static int +get_xregset (struct gdbserv *serv, int pid, void *xregset) +{ + int status; + + status = ptrace_get_fpxregs (serv, pid, xregset); + return status; +} + +/* Function: put_xregset + write the gregset for pid. + Return -1 for failure, zero for success. */ + +static int +put_xregset (struct gdbserv *serv, int pid, const void *xregset) +{ + int status; + + status = ptrace_set_fpxregs (serv, pid, xregset); + + return status; +} + +#else /* ---------------------------------------------------------- */ + +/* These functions may be used for architectures whose registers + are of uniform size and whose registers are all accessible via + PTRACE_PEEKUSER and PTRACE_POKEUSER. */ + +/* Register offset in user struct, or -1 if bad reg num. */ +static long +linux_register_offset (int regnum) +{ + if (regnum < 0 || regnum >= SIZEOF_REGMAP) + return -1; + else if (regmap[regnum] < 0) + return -1; + else +#if defined(_MIPSEL) || defined(_MIPSEB) + /* MIPS implementations of PTRACE_PEEKUSER address register 0 + at offset 0, register 1 at offset 1, etc. Do NOT multiply + the register number by the register size. */ + + return regmap[regnum]; +#else + return SIZEOF_MAPPEDREG * regmap[regnum]; +#endif +} + +/* fetch registers vector. + + NOTE: registers do not generalize well in ptrace, so + we're gonna handle them separately per target. */ + + +enum { U_REGS_OFFSET = 0 }; /* FIXME??? */ + +/* Function: linux_read_reg + Return -1 for failure, zero for success. */ + +static int +linux_read_reg (struct gdbserv *serv, int pid, int regno, + ptrace_xfer_type *regval) +{ + unsigned long u_regs_base = U_REGS_OFFSET; + ptrace_arg3_type regaddr; + + if ((regaddr = linux_register_offset (regno)) < 0) + return -1; /* fail */ + + regaddr += U_REGS_OFFSET; + errno = 0; + ptrace_read_user (serv, pid, regaddr, sizeof (*regval), regval); + + if (errno) + { + fprintf (stderr, "PT_READ_U 0x%08lx from 0x%08lx in process %d\n", + (long) *regval, (long) regaddr, pid); + return -1; + } + else + return 0; +} + +/* Function: linux_write_reg + Return -1 for failure, zero for success. */ + +static int +linux_write_reg (struct gdbserv *serv, int regno, ptrace_xfer_type regval) +{ + unsigned long u_regs_base = U_REGS_OFFSET; + ptrace_arg3_type regaddr; + struct child_process *process = gdbserv_target_data (serv); + + if ((regaddr = linux_register_offset (regno)) < 0) + return -1; /* fail */ + + regaddr += U_REGS_OFFSET; + errno = 0; + ptrace_write_user (serv, process->pid, regaddr, sizeof (regval), ®val); + if (errno) + { + fprintf (stderr, "PT_WRITE_U 0x%08lx from 0x%08lx in process %d\n", + (long) regval, (long) regaddr, process->pid); + return -1; + } + else + return 0; +} + +/* Helper function for debug_get_pc(). It fetches and returns the + value of REGNO. */ +static unsigned long +debug_get_reg (struct gdbserv *serv, pid_t pid, int regno) +{ + ptrace_xfer_type value; + + if (linux_read_reg (serv, pid, regno, &value) < 0) + return 0; + else + return (unsigned long) value; +} + +/* Return the value of PC. Needed by thread-db.c. */ +unsigned long +debug_get_pc (struct gdbserv *serv, pid_t pid) +{ + return debug_get_reg (serv, pid, PC_REGNUM); +} + +/* Function: reg_format + + This is an old, decrepit version that we want to eliminate. If you + find yourself needing to fix a bug in it, consider changing your + target to use one of the preferred mechanisms instead. + + Return zero for success, -1 for failure (never fails). */ + +static int +linux_reg_format (struct gdbserv *serv, + struct gdbserv_thread *thread, + int reg, int *size, int *padding) +{ + /* Size and formatting (padding) of thread in g/G packet. */ + *size = SIZEOF_MAPPEDREG; + *padding = 0; + return 0; +} + +/* Function: linux_sizeof_reg + + This is an old, decrepit version that we want to eliminate. If you + find yourself needing to fix a bug in it, consider changing your + target to use one of the preferred mechanisms instead. + + Returns size of a register. */ + +static long linux_sizeof_reg (struct gdbserv *serv, int regno) +{ + return SIZEOF_MAPPEDREG; +} + +/* Function: linux_get_reg + + This is an old, decrepit version that we want to eliminate. If you + find yourself needing to fix a bug in it, consider changing your + target to use one of the preferred mechanisms instead. + + Return -1 for failure, zero for success. */ + +static int +linux_get_reg (struct gdbserv *serv, int regno, struct gdbserv_reg *reg) +{ + struct child_process *process = gdbserv_target_data (serv); + ptrace_xfer_type regval; + + /* Get the register value. */ + if (linux_read_reg (serv, process->pid, regno, ®val) < 0) + { + fprintf (stderr, "Error: linux_get_reg: Register %d out of bounds.\n", regno); + return -1; + } + + /* Shove it into the gdbserv_reg struct. */ + if (SIZEOF_MAPPEDREG == 4) + gdbserv_ulong_to_reg (serv, (unsigned long) regval, reg); + else + gdbserv_ulonglong_to_reg (serv, (unsigned long long) regval, reg); + return 0; /* success */ + +} + +/* Function: linux_set_reg + + This is an old, decrepit version that we want to eliminate. If you + find yourself needing to fix a bug in it, consider changing your + target to use one of the preferred mechanisms instead. + + Return -1 for failure, zero for success. */ + +static int +linux_set_reg (struct gdbserv *serv, int regno, struct gdbserv_reg *reg) +{ + ptrace_xfer_type regval; + + if (regno < 0 || regno >= NUM_REGS) + { + fprintf (stderr, "Error: linux_set_reg: Register %d out of bounds.\n", regno); + return -1; + } + + /* Get the register value out of the struct gdbserv_reg. */ + if (sizeof (regval) == 4) + gdbserv_reg_to_ulong (serv, reg, (unsigned long *) ®val); + else + gdbserv_reg_to_ulonglong (serv, reg, (unsigned long long *) ®val); + + /* Write the child's register. */ + if (linux_write_reg (serv, regno, regval) < 0) + return -1; /* Fail */ + + return 0; /* success */ + +} + +/* gregset functions */ + +/* Function: reg_from_gregset + Extract register from gregset by regnum. + Return -1 for failure, zero for success. */ + +extern int +reg_from_gregset (struct gdbserv *serv, + struct gdbserv_reg *reg, + int regno, + const GREGSET_T gregset) +{ + unsigned long offset = linux_register_offset (regno); + unsigned long long value; + + if (offset < 0) + return -1; /* failure */ + + if (SIZEOF_MAPPEDREG == 4) + { + value = *((unsigned long *) (((char *) gregset) + offset)); + } + else if (SIZEOF_MAPPEDREG == 8) + { + value = *((unsigned long long *) (((char *) gregset) + offset)); + } + else + { + return -1; /* failure */ + } + gdbserv_ulonglong_to_reg (serv, value, reg); + return 0; +} + +/* Function: reg_to_gregset + Insert register into gregset by regnum. + Return -1 for failure, zero for success. */ + +extern int +reg_to_gregset (struct gdbserv *serv, + const struct gdbserv_reg *reg, + int regno, + GREGSET_T gregset) +{ + unsigned long offset = linux_register_offset (regno); + unsigned long long value; + + if (offset < 0) + return -1; /* failure */ + + gdbserv_reg_to_ulonglong (serv, reg, &value); + if (SIZEOF_MAPPEDREG == 4) + { + *((unsigned long *) (((char *) gregset) + offset)) = value; + } + else if (SIZEOF_MAPPEDREG == 8) + { + *((unsigned long long *) (((char *) gregset) + offset)) = value; + } + else + { + return -1; /* failure */ + } + return 0; +} + +/* Function: reg_from_fpregset + Extract register from fpregset by regnum. + Return -1 for failure, zero for success. */ + +extern int +reg_from_fpregset (struct gdbserv *serv, + struct gdbserv_reg *reg, + int regno, + const FPREGSET_T *fpregset) +{ + return -1; /* Unimplemented. */ +} + +/* Function: reg_to_fpregset + Insert register into fpregset by regnum. + Return -1 for failure, zero for success. */ + +extern int +reg_to_fpregset (struct gdbserv *serv, + const struct gdbserv_reg *reg, + int regno, + FPREGSET_T *fpregset) +{ + return -1; /* Unimplemented. */ +} + +/* Function: reg_from_xregset + Extract register from extended regset by regnum. + Return -1 for failure, zero for success. */ + +int +reg_from_xregset (struct gdbserv *serv, + struct gdbserv_reg *reg, + int regno, + const void *xregset) +{ + return -1; /* Unimplemented. */ +} + +/* Function: reg_to_xregset + Insert register into extended regset by regnum. + Return -1 for failure, zero for success. */ + +int +reg_to_xregset (struct gdbserv *serv, + const struct gdbserv_reg *reg, + int regno, + void *xregset) +{ + return -1; /* Unimplemented. */ +} + +/* Function: get_gregset + Read the gregset for pid. + Return -1 for failure, zero for success. */ + +static int +get_gregset (struct gdbserv *serv, int pid, GREGSET_T gregset) +{ + if (ptrace_read_user (serv, pid, 0, sizeof (GREGSET_T), (char *) gregset) + != 0) + return -1; + return 0; +} + +/* Function: put_gregset + write the gregset for pid. + Return -1 for failure, zero for success. */ + +static int +put_gregset (struct gdbserv *serv, int pid, const GREGSET_T gregset) +{ + if (ptrace_write_user (serv, pid, 0, sizeof (GREGSET_T), + (char *) gregset) != 0) + return -1; + return 0; +} + +static int +get_fpregset (struct gdbserv *serv, int pid, FPREGSET_T *fpregset) +{ + return -1; /* Unimplemented. */ +} + +/* Function: put_fpregset + write the gregset for pid. + Return -1 for failure, zero for success. */ + +static int +put_fpregset (struct gdbserv *serv, int pid, const FPREGSET_T *fpregset) +{ + return -1; /* Unimplemented. */ +} + +/* Function: get_fpregset + Read the fpregset for pid. + Return -1 for failure, zero for success. */ + +static int +get_xregset (struct gdbserv *serv, int pid, void *fpregset) +{ + return -1; +} + +/* Function: put_xregset + write the gregset for pid. + Return -1 for failure, zero for success. */ + +static int +put_xregset (struct gdbserv *serv, int pid, const void *fpregset) +{ + return -1; +} + +#endif /* Target specific register functions for other architectures */ + + +/* Track sole connection to a remote gdb client. */ +/* FIXME: needed? */ +static struct gdbserv *linux_connect_lock = NULL; + + + +/* target vector: */ + +/* private sub-vector for ptrace target: */ +static struct gdbserv_target *ptrace_target; /* FIXME global */ + +/* Detach vector -- shut down this target connection. + */ + +static void +linux_detach (struct gdbserv *serv, struct gdbserv_target *target) +{ + struct child_process *process = gdbserv_target_data (serv); + + assert (linux_connect_lock == serv); + + ptrace_target->detach (serv, target); + if (process->debug_informational) + fprintf (stderr, "linux - detached.\n"); + linux_connect_lock = NULL; + + /* Quit out of main loop for this demo. In general, this is not + necessary, as the next incoming connection could again be handled + by linux_attach() above. */ + server_quit_p = 1; +} + + +/* Process Rcmd vector + * (apparently a target-specific remote command). + */ + +static void +linux_process_rcmd (struct gdbserv *serv, const char *cmd, int cmdsize) +{ + struct child_process *process = gdbserv_target_data (serv); + + if (!strcmp (cmd, "1")) + { + process->debug_backend = 1; + } + else if (!strcmp (cmd, "0")) + { + process->debug_backend = 0; + } +} + +/* This function is called from gdbloop_poll when a new incoming + connection is attempted. It may return NULL if the new connection + is to be refused, or a gdbserv_target vector if the connection is + accepted. */ + +static struct gdbserv_target * +linux_attach (struct gdbserv *serv, void *data) +{ + struct gdbserv_target *linux_target; + struct child_process *process = data; + extern struct gdbserv_target *ptrace_attach (struct gdbserv *, void *); + extern int thread_db_attach (); /* FIXME header <^ */ + + /* Enable server tracing. */ + /* gdbserv_state_trace = stderr;*/ + + if (linux_connect_lock != NULL) + { + fprintf (stderr, "linux: rejected duplicate connection.\n"); + return NULL; + } + + if ((ptrace_target = ptrace_attach (serv, data)) == NULL) + { + fprintf (stderr, "Linux: unable to open %s\n", process->argv[0]); + return NULL; + } + + if (process->debug_informational) + fprintf (stderr, "linux: accepted gdb connection.\n"); + linux_connect_lock = serv; + + linux_target = malloc (sizeof (struct gdbserv_target)); + memset (linux_target, 0, sizeof (*linux_target)); + + /* Callback structure for function pointers that handle processed + control packets. See gdbserv-target.h for docs on the individual + functions. */ + + linux_target->process_rcmd = linux_process_rcmd; + linux_target->process_set_args = NULL; + /* Replace the process_get_reg(s) method with get_regs. */ + linux_target->process_set_reg = NULL; + linux_target->process_set_regs = NULL; + linux_target->process_get_reg = NULL; + linux_target->process_get_regs = NULL; + linux_target->get_reg = linux_get_reg; + linux_target->set_reg = linux_set_reg; + linux_target->sizeof_reg = linux_sizeof_reg; + linux_target->gg_reg_nr = linux_gg_reg_nr; +#if __LITTLE_ENDIAN == __BYTE_ORDER + linux_target->output_reg = gdbserv_output_reg_leb; + linux_target->input_reg = gdbserv_input_reg_leb; +#elif __BIG_ENDIAN == __BYTE_ORDER + linux_target->output_reg = gdbserv_output_reg_beb; + linux_target->input_reg = gdbserv_input_reg_beb; +#else +#error unknown endianness +#endif + linux_target->next_gg_reg = linux_next_gg_reg; + linux_target->reg_format = linux_reg_format; + + linux_target->expedited_reg_nr = NULL; + linux_target->get_mem = ptrace_target->get_mem; + linux_target->set_mem = ptrace_target->set_mem; + linux_target->process_set_pc = NULL; + linux_target->flush_i_cache = ptrace_target->flush_i_cache; + linux_target->process_signal = ptrace_target->process_signal; + linux_target->compute_signal = ptrace_target->compute_signal; + linux_target->get_trap_number = ptrace_target->get_trap_number; + linux_target->exit_program = ptrace_target->exit_program; + linux_target->break_program = ptrace_target->break_program; + linux_target->reset_program = NULL; + linux_target->restart_program = NULL; +#if defined(_MIPSEL) || defined(_MIPSEB) + linux_target->singlestep_program = mips_singlestep_program; +#else + linux_target->singlestep_program = ptrace_target->singlestep_program; +#endif + linux_target->cyclestep_program = NULL; + linux_target->sigkill_program = NULL; + linux_target->continue_program = ptrace_target->continue_program; + linux_target->remove_breakpoint = NULL; + linux_target->set_breakpoint = NULL; + linux_target->process_target_packet = NULL; + linux_target->detach = linux_detach; + + linux_target->data = data; + process->serv = serv; + +#if defined (MAKE_ARCH) + process->arch = MAKE_ARCH (); +#else + process->arch = 0; +#endif + + if (process->arch + && process->arch->make_bp_table) + process->breakpoint_table + = process->arch->make_bp_table (process->arch, serv, linux_target); + else + process->breakpoint_table = 0; + +#if defined(_MIPSEL) || defined(_MIPSEB) + process->is_ss = 0; +#endif + + /* Attach to thread_db module. */ + /* FIXME: unconditional call to extern function? */ + if ((thread_db_attach (serv, linux_target)) < 0) + { /* failed */ + fprintf (stderr, "Failed to open thread_db library.\n"); + } + + return linux_target; +} + +static int +linux_check_child_state (struct child_process *process) +{ + return ptrace_check_child_state (process); +} + +static void +linux_fromtarget_break (struct child_process *process) +{ + int gdb_signal = ptrace_target->compute_signal (process->serv, + process->stop_signal); + gdbserv_fromtarget_break (process->serv, gdb_signal); +} + +static void +linux_fromtarget_exit (struct child_process *process) +{ + gdbserv_fromtarget_exit (process->serv, process->stop_signal); +} + +static void +linux_fromtarget_terminate (struct child_process *process) +{ + int gdb_signal = ptrace_target->compute_signal (process->serv, + process->stop_signal); + + gdbserv_fromtarget_terminate (process->serv, gdb_signal); +} + +/* This struct contains the vectors that connect us to main: + */ +struct server_vector gdbserver = +{ + linux_attach, + linux_check_child_state, + linux_fromtarget_break, + linux_fromtarget_exit, + linux_fromtarget_terminate +}; + +/* Function: decr_pc_after_break + [Must be implemented explicitly for each supported architecture.] + + For multi-thread linux, if several threads hit a breakpoint + "simultaneously", we will want to set at least one of them up + so that it will hit the same breakpoint again the next time it + gets to run (assuming the breakpoint hasn't been removed by then). + + Depending on the target architecture, that may mean setting + the PC back to point at the trap instruction. + + Return -1 for failure, zero for success. */ + +#ifdef X86_LINUX_TARGET + +/* For ia32 we do need to decrement the PC to point at the trap. */ +int +decr_pc_after_break (struct gdbserv *serv, pid_t pid) +{ + extern int thread_db_noisy; + unsigned long pc; + int status; + + status = read_reg_as_ulong (serv, pid, PC_REGNUM, &pc); + if (status < 0) + return -1; + + pc -= 1; + if (thread_db_noisy) + fprintf (stderr, "<decr_pc_after_break: pid %d, addr 0x%08x>\n", pid, pc); + status = write_reg_as_ulong (serv, pid, PC_REGNUM, pc); + return status; +} + +#else +int +decr_pc_after_break (struct gdbserv *serv, pid_t pid) +{ + return 0; +} +#endif + + +#if defined(_MIPSEL) || defined(_MIPSEB) + +/* + * Worker function to get and return a register + */ + +static ptrace_xfer_type +mips_get_reg(struct gdbserv *serv, int pid, int regno) +{ + ptrace_xfer_type value; + + if (read_reg_bytes (serv, pid, regno, &value) < 0) + return 0; + else + return value; +} + +static struct gdbserv_reg +mips_addr_as_reg (struct gdbserv *serv, ptrace_arg3_type addr) +{ + struct gdbserv_reg addr_as_reg; + + gdbserv_host_bytes_to_reg (serv, &addr, sizeof (addr), + &addr_as_reg, sizeof (ptrace_arg3_type), + sign_extend); + return addr_as_reg; +} + +/* peek / poke mips instructions. Using an ``unsigned int'' to represent + a mips instruction is correct (with regard to size) for the o32, n32, + and n64 ABIs. */ +static unsigned int +mips_peek_instruction (struct gdbserv *serv, ptrace_arg3_type addr) +{ + struct gdbserv_reg addr_as_reg; + unsigned int insn; + + addr_as_reg = mips_addr_as_reg (serv, addr); + ptrace_get_mem (serv, &addr_as_reg, &insn, sizeof (insn)); + return insn; +} + +static void +mips_poke_instruction (struct gdbserv *serv, ptrace_arg3_type addr, + unsigned int insn) +{ + struct gdbserv_reg addr_as_reg; + + addr_as_reg = mips_addr_as_reg (serv, addr); + ptrace_set_mem (serv, &addr_as_reg, &insn, sizeof (insn)); +} + +/* + * mips singlestep + * + * necessary since no support in ptrace. + */ +static void +mips_singlestep_program (struct gdbserv *serv) +{ + struct child_process *process = gdbserv_target_data (serv); + + mips_singlestep (serv, process->pid, process->signal_to_send); + process->stop_signal = 0; + process->stop_status = 0; + process->signal_to_send = 0; +} + +int +mips_singlestep (struct gdbserv *serv, pid_t pid, int sig) +{ + struct child_process *process = gdbserv_target_data (serv); + ptrace_arg3_type targ; + ptrace_xfer_type mips_pc; + + union mips_instruction insn; + int is_branch, is_cond, i; + + unsigned int bp_inst = 0x0000000d; + + /* FIXME: handle signals! */ + if (process->debug_backend) + fprintf (stderr, "mips_singlestep %d %ld\n", pid, sig); + + errno = 0; + + /* Following is equiv to ptrace (PTRACE_SINGLESTEP, pid, 1L, sig); */ + + /* get the current PC */ + mips_pc = mips_get_reg(serv, pid, PC_REGNUM); + targ = mips_pc; + + /* get the word there (opcode) */ + + insn.word = mips_peek_instruction (serv, mips_pc); + + is_branch = is_cond = 0; + + /* set flag so handle_waitstatus can restore breakpoint stuff */ + process->is_ss = 1; + + switch (insn.i_format.opcode) { + /* + * jr and jalr are in r_format format. + */ + case spec_op: + switch (insn.r_format.func) { + case jalr_op: + case jr_op: + targ = mips_get_reg(serv, pid, insn.r_format.rs); + is_branch = 1; + break; + } + break; + + /* + * This group contains: + * bltz_op, bgez_op, bltzl_op, bgezl_op, + * bltzal_op, bgezal_op, bltzall_op, bgezall_op. + */ + case bcond_op: + is_branch = is_cond = 1; + targ += 4 + (insn.i_format.simmediate << 2); + break; + + /* + * These are unconditional and in j_format. + */ + case jal_op: + case j_op: + is_branch = 1; + targ += 4; + targ >>= 28; + targ <<= 28; + targ |= (insn.j_format.target << 2); + break; + + /* Some cop1 instructions are conditional branches. */ + case cop1_op: + if (insn.i_format.rs == bc_op + || insn.i_format.rs == bc_op + 1 /* e.g, BC1ANY2 on MIPS-3D */ + || insn.i_format.rs == bc_op + 2 /* e.g, BC1ANY4 on MIPS-3D */) + { + is_branch = is_cond = 1; + targ += 4 + (insn.i_format.simmediate << 2); + } + break; + + /* Some cop2 instructions are conditional branches. */ + case cop2_op: + /* MIPS32 Architecture For Programmers Volume II, rev 1.90 documents + bc2f, bc2fl, bc2t, and bc2tl. */ + if (insn.i_format.rs == bc_op) + { + is_branch = is_cond = 1; + targ += 4 + (insn.i_format.simmediate << 2); + } + break; + + /* Other conditional branches... */ + case beq_op: + case beql_op: + case bne_op: + case bnel_op: + case blez_op: + case blezl_op: + case bgtz_op: + case bgtzl_op: + is_branch = is_cond = 1; + targ += 4 + (insn.i_format.simmediate << 2); + break; + } + + process->ss_info[1].in_use = 0; /* Mark unused. */ + if (is_branch) + { + i = 0; + if (is_cond && targ != (mips_pc + 8)) + { + process->ss_info[i].in_use = 1; + process->ss_info[i].ss_addr = mips_addr_as_reg (serv, mips_pc + 8); + process->ss_info[i++].ss_val + = mips_peek_instruction (serv, mips_pc + 8); + mips_poke_instruction (serv, mips_pc + 8, bp_inst); + } + process->ss_info[i].in_use = 1; + process->ss_info[i].ss_addr = mips_addr_as_reg (serv, targ); + process->ss_info[i].ss_val = mips_peek_instruction (serv, targ); + mips_poke_instruction (serv, targ, bp_inst); + } + else + { + process->ss_info[0].in_use = 1; + process->ss_info[0].ss_addr = mips_addr_as_reg (serv, mips_pc + 4); + process->ss_info[0].ss_val = mips_peek_instruction (serv, mips_pc + 4); + mips_poke_instruction (serv, mips_pc + 4, bp_inst); + } + + ptrace (PTRACE_CONT, pid, 1L, sig); + return 0; +} +#endif /* _MIPSEL */ + + +/* proc_service callback functions */ + +ps_err_e +ps_lgetregs (gdb_ps_prochandle_t ph, /* Get LWP general regs */ + lwpid_t lwpid, + GREGSET_T gregset) +{ + if (get_gregset (ph->serv, lwpid, gregset) < 0) + { + fprintf (stderr, "<<< ERROR ps_lgetregs %d >>>\n", lwpid); + return PS_ERR; + } + return PS_OK; +} + +ps_err_e +ps_lsetregs (gdb_ps_prochandle_t ph, /* Set LWP general regs */ + lwpid_t lwpid, + const GREGSET_T gregset) +{ + if (put_gregset (ph->serv, lwpid, gregset) < 0) + { + fprintf (stderr, "<<< ERROR ps_lsetregs %d >>>\n", lwpid); + return PS_ERR; + } + return PS_OK; +} + +ps_err_e +ps_lgetfpregs (gdb_ps_prochandle_t ph, /* Get LWP float regs */ + lwpid_t lwpid, + FPREGSET_T *fpregset) +{ + if (get_fpregset (ph->serv, lwpid, fpregset) < 0) + { + fprintf (stderr, "<<< ERROR ps_lgetfpregs %d >>>\n", lwpid); + return PS_ERR; + } + return PS_OK; +} + +ps_err_e +ps_lsetfpregs (gdb_ps_prochandle_t ph, /* Set LWP float regs */ + lwpid_t lwpid, + const FPREGSET_T *fpregset) +{ + if (put_fpregset (ph->serv, lwpid, fpregset) < 0) + { + fprintf (stderr, "<<< ERROR ps_lsetfpregs %d >>>\n", lwpid); + return PS_ERR; + } + return PS_OK; +} + +ps_err_e +ps_lgetxregsize (gdb_ps_prochandle_t ph, /* Get XREG size */ + lwpid_t lwpid, + int *xregsize) +{ + *xregsize = get_xregsetsize (ph->serv, lwpid); + if (*xregsize > 0) + return PS_OK; + else + return PS_ERR; +} + +ps_err_e +ps_lgetxregs (gdb_ps_prochandle_t ph, /* Get XREGS */ + lwpid_t lwpid, + caddr_t xregset) +{ + if (get_xregset (ph->serv, lwpid, xregset) < 0) + { + fprintf (stderr, "<<< ERROR ps_lgetxregs %d >>>\n", lwpid); + return PS_ERR; + } + return PS_OK; +} + +ps_err_e +ps_lsetxregs (gdb_ps_prochandle_t ph, /* Set XREGS */ + lwpid_t lwpid, + caddr_t xregset) +{ + if (put_xregset (ph->serv, lwpid, xregset) < 0) + { + fprintf (stderr, "<<< ERROR ps_lsetxregs %d >>>\n", lwpid); + return PS_ERR; + } + return PS_OK; +} + +/* + * ps_getpid + * + * return the main pid for the child process + * (special for Linux -- not used on Solaris) + */ + +pid_t +ps_getpid (gdb_ps_prochandle_t ph) +{ + return ph->pid; +} |