diff options
author | obrien <obrien@FreeBSD.org> | 2001-03-25 02:20:37 +0000 |
---|---|---|
committer | obrien <obrien@FreeBSD.org> | 2001-03-25 02:20:37 +0000 |
commit | d4c331dbb7ac9fd1dbba6721308bd1bd072574d2 (patch) | |
tree | f7ba85376677a04f2d7a2cfeac2a3817bcd9e29e /gnu | |
parent | e0c89bb23429639a2a3ea177bdbdb4ff93dc857b (diff) | |
download | FreeBSD-src-d4c331dbb7ac9fd1dbba6721308bd1bd072574d2.zip FreeBSD-src-d4c331dbb7ac9fd1dbba6721308bd1bd072574d2.tar.gz |
Temporary build structure for GDB 5.0 so people can test the new version
before pulling the switch to making it the default version.
Diffstat (limited to 'gnu')
21 files changed, 4714 insertions, 0 deletions
diff --git a/gnu/usr.bin/binutils/gdb5/Makefile b/gnu/usr.bin/binutils/gdb5/Makefile new file mode 100644 index 0000000..b7be700 --- /dev/null +++ b/gnu/usr.bin/binutils/gdb5/Makefile @@ -0,0 +1,115 @@ +# $FreeBSD$ + +.include "../Makefile.inc0" + +.PATH: ${SRCDIR}/binutils + +.if ${MACHINE_ARCH} == "i386" +CFLAGS+= -Dprint_insn_i386=print_insn_i386_att +.endif + +PROG= gdb +XSRCS= annotate.c ax-general.c ax-gdb.c bcache.c blockframe.c \ + breakpoint.c buildsym.c c-exp.y c-lang.c c-typeprint.c \ + c-valprint.c ch-exp.c ch-lang.c ch-typeprint.c ch-valprint.c \ + coffread.c command.c complaints.c copying.c core-regset.c \ + corefile.c corelow.c cp-valprint.c dcache.c dbxread.c \ + demangle.c dwarfread.c dwarf2read.c elfread.c environ.c eval.c \ + exec.c expprint.c f-exp.y f-lang.c f-typeprint.c f-valprint.c \ + findvar.c fork-child.c freebsd-uthread.c gdbarch.c gdbtypes.c \ + infcmd.c inflow.c infptrace.c infrun.c inftarg.c language.c \ + jv-exp.y jv-lang.c jv-valprint.c jv-typeprint.c nlmread.c \ + m2-lang.c m2-exp.y m2-typeprint.c m2-valprint.c main.c maint.c \ + mdebugread.c mem-break.c minsyms.c objfiles.c parse.c \ + printcmd.c remote.c remote-utils.c scm-exp.c scm-lang.c \ + scm-valprint.c solib.c source.c stabsread.c stack.c symfile.c \ + symmisc.c symtab.c target.c thread.c top.c tracepoint.c \ + typeprint.c utils.c valarith.c valops.c valprint.c values.c \ + version.c serial.c ser-unix.c ser-tcp.c +SRCS= init.c ${XSRCS} +SRCS+= wait.h + +.if exists(${.CURDIR}/Makefile.${MACHINE_ARCH}) +.include "${.CURDIR}/Makefile.${MACHINE_ARCH}" +.endif + +#CFLAGS+= -I${.CURDIR}/${RELTOP}/libbinutils +CFLAGS+= -I${.CURDIR}/${MACHINE_ARCH} +CFLAGS+= -I${SRCDIR}/binutils +CFLAGS+= -I${SRCDIR}/bfd +CFLAGS+= -I${GDBDIR}/gdb +CFLAGS+= -I${GDBDIR}/gdb/config +DPADD= ${RELTOP}/libbfd/libbfd.a +DPADD+= ${RELTOP}/libopcodes/libopcodes.a +DPADD+= ${LIBREADLINE} +DPADD+= ${LIBGNUREGEX} +DPADD+= ${RELTOP}/libiberty/libiberty.a +DPADD+= ${LIBTERMCAP} +LDADD+= ${RELTOP}/libbfd/libbfd.a +LDADD+= ${RELTOP}/libopcodes/libopcodes.a +LDADD+= -lreadline +LDADD+= -lgnuregex +LDADD+= ${RELTOP}/libiberty/libiberty.a +LDADD+= -ltermcap + +GDBDIR= ${.CURDIR}/../../../../contrib/gdb.291 +.PATH: ${GDBDIR}/gdb +.PATH: ${SRCDIR}/opcodes + +CFLAGS+= -DFREEBSD_ELF + +CFLAGS+= -I$(.CURDIR) -I${DESTDIR}/usr/include/readline +# use phkmalloc +CFLAGS+= -DNO_MMALLOC +# uncomment the next line if you want to debug gdb +#CFLAGS+= -g +YFLAGS= + +CLEANFILES= init.c init.c-tmp wait.h + +# We do this by grepping through sources. If that turns out to be too slow, +# maybe we could just require every .o file to have an initialization routine +# of a given name (remote-udi.o -> _initialize_remote_udi, etc.). +# +# Formatting conventions: The name of the _initialize_* routines must start +# in column zero, and must not be inside #if. +# +# Note that the set of files with init functions might change, or the names +# of the functions might change, so this files needs to depend on all the +# object files that will be linked into gdb. + +init.c: ${XSRCS} + @${ECHO} Making ${.TARGET} + @rm -f init.c-tmp + @echo '/* Do not modify this file. */' >init.c-tmp + @echo '/* It is created automatically by the Makefile. */'>>init.c-tmp + @echo 'void initialize_all_files () {' >>init.c-tmp + @for i in ${.ALLSRC} ; do \ + filename=`echo $$i | sed \ + -e '/^Onindy.c/d' \ + -e '/^nindy.c/d' \ + -e '/ttyflush.c/d' \ + -e '/xdr_ld.c/d' \ + -e '/xdr_ptrace.c/d' \ + -e '/xdr_rdb.c/d' \ + -e '/udr.c/d' \ + -e '/udip2soc.c/d' \ + -e '/udi2go32.c/d' \ + -e '/version.c/d' \ + -e '/^[a-z0-9A-Z_]*_[SU].c/d' \ + -e '/[a-z0-9A-Z_]*-exp.tab.c/d'` ; \ + case $$filename in \ + "") ;; \ + *) sed <$$filename >>init.c-tmp -n \ + -e '/^_initialize_[a-z_0-9A-Z]* *(/s/^\([a-z_0-9A-Z]*\).*/ {extern void \1 (); \1 ();}/p' ; ;; \ + esac ; \ + done + @echo '}' >>init.c-tmp + @mv init.c-tmp ${.TARGET} + +.PRECIOUS: init.c + +wait.h: + ln -sf ${.CURDIR}/../../../../sys/sys/wait.h ${.TARGET} + +.include <bsd.prog.mk> diff --git a/gnu/usr.bin/binutils/gdb5/Makefile.alpha b/gnu/usr.bin/binutils/gdb5/Makefile.alpha new file mode 100644 index 0000000..bb11105 --- /dev/null +++ b/gnu/usr.bin/binutils/gdb5/Makefile.alpha @@ -0,0 +1,8 @@ +# +# $FreeBSD$ +# + +XSRCS+= freebsd-nat.c alpha-tdep.c kvm-fbsd.c +LDADD+= -lkvm + +.PATH: ${.CURDIR}/alpha diff --git a/gnu/usr.bin/binutils/gdb5/Makefile.i386 b/gnu/usr.bin/binutils/gdb5/Makefile.i386 new file mode 100644 index 0000000..607463b --- /dev/null +++ b/gnu/usr.bin/binutils/gdb5/Makefile.i386 @@ -0,0 +1,7 @@ +# +# $FreeBSD$ +# + +XSRCS+= freebsd-nat.c i386-tdep.c i387-tdep.c kvm-fbsd.c + +.PATH: ${.CURDIR}/i386 diff --git a/gnu/usr.bin/binutils/gdb5/Makefile.ia64 b/gnu/usr.bin/binutils/gdb5/Makefile.ia64 new file mode 100644 index 0000000..160e966 --- /dev/null +++ b/gnu/usr.bin/binutils/gdb5/Makefile.ia64 @@ -0,0 +1,8 @@ +# +# $FreeBSD$ +# + +XSRCS+= freebsd-nat.c ia64-tdep.c kvm-fbsd.c +LDADD+= -lkvm + +.PATH: ${.CURDIR}/ia64 diff --git a/gnu/usr.bin/binutils/gdb5/alpha/freebsd-nat.c b/gnu/usr.bin/binutils/gdb5/alpha/freebsd-nat.c new file mode 100644 index 0000000..4bfd8ed --- /dev/null +++ b/gnu/usr.bin/binutils/gdb5/alpha/freebsd-nat.c @@ -0,0 +1,175 @@ +/* Native-dependent code for BSD Unix running on i386's, for GDB. + Copyright 1988, 1989, 1991, 1992, 1994, 1996 Free Software Foundation, Inc. + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 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. */ + +/* $FreeBSD$ */ + +#include "defs.h" + +#include <sys/types.h> +#include <sys/ptrace.h> +#include <machine/reg.h> +#include <machine/frame.h> +#include <sys/param.h> +#include <sys/user.h> +#include <string.h> +#include "gdbcore.h" +#include "value.h" +#include "inferior.h" + +#if defined(HAVE_GREGSET_T) +#include <sys/procfs.h> +#endif + +int kernel_debugging = 0; + +/* Size of elements in jmpbuf */ + +#define JB_ELEMENT_SIZE 8 + +/* The definition for JB_PC in machine/reg.h is wrong. + And we can't get at the correct definition in setjmp.h as it is + not always available (eg. if _POSIX_SOURCE is defined which is the + default). As the defintion is unlikely to change (see comment + in <setjmp.h>, define the correct value here. */ + +#undef JB_PC +#define JB_PC 2 + +/* Figure out where the longjmp will land. + We expect the first arg to be a pointer to the jmp_buf structure from which + we extract the pc (JB_PC) that we will land at. The pc is copied into PC. + This routine returns true on success. */ + +int +get_longjmp_target (pc) + CORE_ADDR *pc; +{ + CORE_ADDR jb_addr; + char raw_buffer[MAX_REGISTER_RAW_SIZE]; + + jb_addr = read_register(A0_REGNUM); + + if (target_read_memory(jb_addr + JB_PC * JB_ELEMENT_SIZE, raw_buffer, + sizeof(CORE_ADDR))) + return 0; + + *pc = extract_address (raw_buffer, sizeof(CORE_ADDR)); + return 1; +} + +void +fetch_inferior_registers (regno) + int regno; +{ + struct reg regs; /* ptrace order, not gcc/gdb order */ + struct fpreg fpregs; + int r; + + ptrace (PT_GETREGS, inferior_pid, (PTRACE_ARG3_TYPE) ®s, 0); + ptrace (PT_GETFPREGS, inferior_pid, (PTRACE_ARG3_TYPE) &fpregs, 0); + + for (r = 0; r < 31; r++) + memcpy (®isters[REGISTER_BYTE (r)], + ®s.r_regs[r], sizeof(u_int64_t)); + for (r = 0; r < 32; r++) + memcpy (®isters[REGISTER_BYTE (r + FP0_REGNUM)], + &fpregs.fpr_regs[r], sizeof(u_int64_t)); + memcpy (®isters[REGISTER_BYTE (PC_REGNUM)], + ®s.r_regs[31], sizeof(u_int64_t)); + + memset (®isters[REGISTER_BYTE (ZERO_REGNUM)], 0, sizeof(u_int64_t)); + memset (®isters[REGISTER_BYTE (FP_REGNUM)], 0, sizeof(u_int64_t)); + + registers_fetched (); +} + +void +store_inferior_registers (regno) + int regno; +{ + struct reg regs; /* ptrace order, not gcc/gdb order */ + struct fpreg fpregs; + int r; + + for (r = 0; r < 31; r++) + memcpy (®s.r_regs[r], + ®isters[REGISTER_BYTE (r)], sizeof(u_int64_t)); + for (r = 0; r < 32; r++) + memcpy (&fpregs.fpr_regs[r], + ®isters[REGISTER_BYTE (r + FP0_REGNUM)], sizeof(u_int64_t)); + memcpy (®s.r_regs[31], + ®isters[REGISTER_BYTE (PC_REGNUM)], sizeof(u_int64_t)); + + ptrace (PT_SETREGS, inferior_pid, (PTRACE_ARG3_TYPE) ®s, 0); + ptrace (PT_SETFPREGS, inferior_pid, (PTRACE_ARG3_TYPE) &fpregs, 0); +} + +#ifdef HAVE_GREGSET_T +void +supply_gregset (gp) + gregset_t *gp; +{ + int regno = 0; + + /* These must be ordered the same as REGISTER_NAMES in + config/alpha/tm-alpha.h. */ + for (regno = 0; regno < 31; regno++) + supply_register (regno, (char *)&gp->r_regs[regno]); + supply_register (PC_REGNUM, (char *)&gp->r_regs[regno]); +} +#endif /* HAVE_GREGSET_T */ + +#ifdef HAVE_FPREGSET_T +void +supply_fpregset (fp) + fpregset_t *fp; +{ + int regno = 0; + + for (regno = 0; regno < 32; regno++) + supply_register (regno + 32, (char *)&fp->fpr_regs[regno]); +} +#endif /* HAVE_FPREGSET_T */ + +/* + * Get registers from a kernel crash dump or live kernel. + * Called by kvm-fbsd.c:get_kcore_registers(). + */ +fetch_kcore_registers (pcbp) + struct pcb *pcbp; +{ + + /* First clear out any garbage. */ + memset(registers, '\0', REGISTER_BYTES); + + /* SP */ + *(long *) ®isters[REGISTER_BYTE (SP_REGNUM)] = + pcbp->pcb_hw.apcb_ksp; + + /* S0 through S6 */ + memcpy (®isters[REGISTER_BYTE (S0_REGNUM)], + &pcbp->pcb_context[0], 7 * sizeof(long)); + + /* PC */ + *(long *) ®isters[REGISTER_BYTE (PC_REGNUM)] = + pcbp->pcb_context[7]; + + registers_fetched (); +} + diff --git a/gnu/usr.bin/binutils/gdb5/alpha/kvm-fbsd.c b/gnu/usr.bin/binutils/gdb5/alpha/kvm-fbsd.c new file mode 100644 index 0000000..6da1a8e --- /dev/null +++ b/gnu/usr.bin/binutils/gdb5/alpha/kvm-fbsd.c @@ -0,0 +1,438 @@ +/* Kernel core dump functions below target vector, for GDB. + Copyright 1986, 1987, 1989, 1991, 1992, 1993, 1994, 1995 + Free Software Foundation, Inc. + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 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. +*/ + +/* $FreeBSD$ */ + +/* + * This works like "remote" but, you use it like this: + * target kcore /dev/mem + * or + * target kcore /var/crash/host/core.0 + * + * This way makes it easy to short-circut the whole bfd monster, + * and direct the inferior stuff to our libkvm implementation. + */ + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/proc.h> +#include <sys/user.h> +#include <errno.h> +#include <signal.h> +#include <fcntl.h> +#include <kvm.h> +#include <paths.h> + +#include "defs.h" +#include "gdb_string.h" +#include "frame.h" /* required by inferior.h */ +#include "inferior.h" +#include "symtab.h" +#include "command.h" +#include "bfd.h" +#include "target.h" +#include "gdbcore.h" + +static void +kcore_files_info PARAMS ((struct target_ops *)); + +static void +kcore_close PARAMS ((int)); + +static void +get_kcore_registers PARAMS ((int)); + +static int +xfer_mem PARAMS ((CORE_ADDR, char *, int, int, struct target_ops *)); + +static int +xfer_umem PARAMS ((CORE_ADDR, char *, int, int)); + +static char *core_file; +static kvm_t *core_kd; +static struct pcb cur_pcb; + +static struct target_ops kcore_ops; +int kernel_writablecore; + +/* + * Read the "thing" at kernel address 'addr' into the space pointed to + * by point. The length of the "thing" is determined by the type of p. + * Result is non-zero if transfer fails. + */ +#define kvread(addr, p) \ +(target_read_memory((CORE_ADDR)(addr), (char *)(p), sizeof(*(p)))) + + +CORE_ADDR +ksym_lookup(name) + const char *name; +{ + struct minimal_symbol *sym; + + sym = lookup_minimal_symbol(name, NULL, NULL); + if (sym == NULL) + error("kernel symbol `%s' not found.", name); + + return SYMBOL_VALUE_ADDRESS(sym); +} + +/* + * Provide the address of an initial PCB to use. + * If this is a crash dump, try for "dumppcb". + * If no "dumppcb" or it's /dev/mem, use proc0. + * Return the core address of the PCB we found. + */ +static CORE_ADDR +initial_pcb() +{ + struct minimal_symbol *sym; + CORE_ADDR addr; + void *val; + + /* Make sure things are open... */ + if (!core_kd || !core_file) + return (0); + + /* If this is NOT /dev/mem try for dumppcb. */ + if (strncmp(core_file, _PATH_DEV, sizeof _PATH_DEV - 1)) { + sym = lookup_minimal_symbol("dumppcb", NULL, NULL); + if (sym != NULL) { + addr = SYMBOL_VALUE_ADDRESS(sym); + return (addr); + } + } + + /* + * OK, just use proc0pcb. Note that curproc might + * not exist, and if it does, it will point to gdb. + * Therefore, just use proc0 and let the user set + * some other context if they care about it. + */ + addr = ksym_lookup("proc0paddr"); + if (kvread(addr, &val)) { + error("cannot read proc0paddr pointer at %x\n", addr); + val = 0; + } + + return ((CORE_ADDR)val); +} + +/* + * Set the current context to that of the PCB struct + * at the system address passed. + */ +static int +set_context(addr) + CORE_ADDR addr; +{ + + if (kvread(addr, &cur_pcb)) + error("cannot read pcb at %#x", addr); + + /* Fetch all registers from core file */ + target_fetch_registers (-1); + + /* Now, set up the frame cache, and print the top of stack */ + flush_cached_frames(); + set_current_frame (create_new_frame (read_fp (), read_pc ())); + select_frame (get_current_frame (), 0); + return (0); +} + +/* Discard all vestiges of any previous core file and mark data and stack + spaces as empty. */ + +/* ARGSUSED */ +static void +kcore_close (quitting) + int quitting; +{ + + inferior_pid = 0; /* Avoid confusion from thread stuff */ + + if (core_kd) { + kvm_close(core_kd); + free(core_file); + core_file = NULL; + core_kd = NULL; + } +} + +/* This routine opens and sets up the core file bfd. */ + +static void +kcore_open (filename, from_tty) + char *filename; /* the core file */ + int from_tty; +{ + kvm_t *kd; + const char *p; + struct cleanup *old_chain; + char buf[256], *cp; + int ontop; + CORE_ADDR addr; + + target_preopen (from_tty); + + /* The exec file is required for symbols. */ + if (exec_bfd == NULL) + error("No kernel exec file specified"); + + if (core_kd) { + error ("No core file specified." + " (Use `detach' to stop debugging a core file.)"); + return; + } + + if (!filename) { + error ("No core file specified."); + return; + } + + filename = tilde_expand (filename); + if (filename[0] != '/') { + cp = concat (current_directory, "/", filename, NULL); + free (filename); + filename = cp; + } + + old_chain = make_cleanup (free, filename); + + kd = kvm_open (bfd_get_filename(exec_bfd), filename, NULL, + kernel_writablecore ? O_RDWR: O_RDONLY, 0); + if (kd == NULL) { + perror_with_name (filename); + return; + } + + /* Looks semi-reasonable. Toss the old core file and work on the new. */ + + discard_cleanups (old_chain); /* Don't free filename any more */ + core_file = filename; + unpush_target (&kcore_ops); + ontop = !push_target (&kcore_ops); + + /* Note unpush_target (above) calls kcore_close. */ + core_kd = kd; + + /* print out the panic string if there is one */ + if (kvread(ksym_lookup("panicstr"), &addr) == 0 && + addr != 0 && + target_read_memory(addr, buf, sizeof(buf)) == 0) { + + for (cp = buf; cp < &buf[sizeof(buf)] && *cp; cp++) + if (!isascii(*cp) || (!isprint(*cp) && !isspace(*cp))) + *cp = '?'; + *cp = '\0'; + if (buf[0] != '\0') + printf_filtered("panic: %s\n", buf); + } + + if (!ontop) { + warning ( +"you won't be able to access this core file until you terminate\n\ +your %s; do ``info files''", target_longname); + return; + } + + /* Now, set up process context, and print the top of stack */ + (void)set_context(initial_pcb()); + print_stack_frame (selected_frame, selected_frame_level, 1); +} + +static void +kcore_detach (args, from_tty) + char *args; + int from_tty; +{ + if (args) + error ("Too many arguments"); + unpush_target (&kcore_ops); + reinit_frame_cache (); + if (from_tty) + printf_filtered ("No kernel core file now.\n"); +} + +/* Get the registers out of a core file. This is the machine- + independent part. Fetch_core_registers is the machine-dependent + part, typically implemented in the xm-file for each architecture. */ + +/* We just get all the registers, so we don't use regno. */ + +/* ARGSUSED */ +static void +get_kcore_registers (regno) + int regno; +{ + + /* + * XXX - Only read the pcb when set_context() is called. + * When looking at a live kernel this may be a problem, + * but the user can do another "proc" or "pcb" command to + * grab a new copy of the pcb... + */ + + /* + * Zero out register set then fill in the ones we know about. + */ + fetch_kcore_registers (&cur_pcb); +} + +static void +kcore_files_info (t) + struct target_ops *t; +{ + printf_filtered ("\t`%s'\n", core_file); +} + +/* If mourn is being called in all the right places, this could be say + `gdb internal error' (since generic_mourn calls breakpoint_init_inferior). */ + +static int +ignore (addr, contents) + CORE_ADDR addr; + char *contents; +{ + return 0; +} + +static int +xfer_kmem (memaddr, myaddr, len, write, target) + CORE_ADDR memaddr; + char *myaddr; + int len; + int write; + struct target_ops *target; +{ + int n; + +#if 0 /* XXX */ + if (it is a user address) + return xfer_umem(memaddr, myaddr, len, write); +#endif + + if (core_kd == NULL) + return 0; + + if (write) + n = kvm_write(core_kd, memaddr, myaddr, len); + else + n = kvm_read (core_kd, memaddr, myaddr, len) ; + if (n < 0) { + fprintf_unfiltered (gdb_stderr, "can not access 0x%x, %s\n", + memaddr, kvm_geterr(core_kd)); + n = 0; + } + + return n; +} + +#if 0 /* XXX */ +static int +xfer_umem (memaddr, myaddr, len, write) + CORE_ADDR memaddr; + char *myaddr; + int len; + int write; /* ignored */ +{ + int n; + struct proc proc; + + if (kvread(cur_proc, &proc)) + error("cannot read proc at %#x", cur_proc); + n = kvm_uread(core_kd, &proc, memaddr, myaddr, len) ; + + if (n < 0) + return 0; + return n; +} +#endif + +static void +set_proc_cmd(arg) + char *arg; +{ + CORE_ADDR addr; + void *val; + + if (!arg) + error_no_arg("proc address for the new context"); + + if (core_kd == NULL) + error("no kernel core file"); + + addr = (CORE_ADDR)parse_and_eval_address(arg); + + /* Read the PCB address in proc structure. */ + addr += (int) &((struct proc *)0)->p_addr; + if (kvread(addr, &val)) + error("cannot read u area ptr"); + + if (set_context((CORE_ADDR)val)) + error("invalid proc address"); +} + +static void +set_pcb_cmd(arg) + char *arg; +{ + CORE_ADDR addr; + void *val; + + if (!arg) + error_no_arg("pcb address for the new context"); + + if (core_kd == NULL) + error("no kernel core file"); + + addr = (CORE_ADDR)parse_and_eval_address(arg); + + if (set_context(addr)) + error("invalid pcb address"); +} + + + +void +_initialize_kcorelow() +{ + kcore_ops.to_shortname = "kcore"; + kcore_ops.to_longname = "Kernel core dump file"; + kcore_ops.to_doc = + "Use a core file as a target. Specify the filename of the core file."; + kcore_ops.to_open = kcore_open; + kcore_ops.to_close = kcore_close; + kcore_ops.to_attach = find_default_attach; + kcore_ops.to_detach = kcore_detach; + kcore_ops.to_fetch_registers = get_kcore_registers; + kcore_ops.to_xfer_memory = xfer_kmem; + kcore_ops.to_files_info = kcore_files_info; + kcore_ops.to_create_inferior = find_default_create_inferior; + kcore_ops.to_stratum = kcore_stratum; + kcore_ops.to_has_memory = 1; + kcore_ops.to_has_stack = 1; + kcore_ops.to_has_registers = 1; + kcore_ops.to_magic = OPS_MAGIC; + + add_target (&kcore_ops); + add_com ("proc", class_obscure, set_proc_cmd, "Set current process context"); +} diff --git a/gnu/usr.bin/binutils/gdb5/alpha/nm.h b/gnu/usr.bin/binutils/gdb5/alpha/nm.h new file mode 100644 index 0000000..4bc1dea --- /dev/null +++ b/gnu/usr.bin/binutils/gdb5/alpha/nm.h @@ -0,0 +1,96 @@ +/* Native definitions for alpha running FreeBSD. + Copyright (C) 1993, 1994 Free Software Foundation, Inc. + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Figure out where the longjmp will land. We expect that we have just entered + longjmp and haven't yet setup the stack frame, so the args are still in the + argument regs. A0_REGNUM points at the jmp_buf structure from which we + extract the pc (JB_PC) that we will land at. The pc is copied into ADDR. + This routine returns true on success */ + +/* $FreeBSD$ */ + +#define GET_LONGJMP_TARGET(ADDR) get_longjmp_target(ADDR) +extern int +get_longjmp_target PARAMS ((CORE_ADDR *)); + +/* Tell gdb that we can attach and detach other processes */ +#define ATTACH_DETACH + +/* We define our own fetch/store methods */ +#define FETCH_INFERIOR_REGISTERS + +extern CORE_ADDR alpha_u_regs_offset(); +#define U_REGS_OFFSET alpha_u_regs_offset() + +#define PTRACE_ARG3_TYPE char* + +/* ptrace transfers longs, the ptrace man page is lying. */ + +#define PTRACE_XFER_TYPE int + +/* The alpha does not step over a breakpoint, the manpage is lying again. */ + +#define CANNOT_STEP_BREAKPOINT + +/* Linux has shared libraries. */ + +#define GDB_TARGET_HAS_SHARED_LIBS + +/* Support for shared libraries. */ + +#include "solib.h" + +#ifdef __ELF__ +#define SVR4_SHARED_LIBS +#define TARGET_ELF64 +#endif + +/* This is a lie. It's actually in stdio.h. */ + +#define PSIGNAL_IN_SIGNAL_H + +/* Given a pointer to either a gregset_t or fpregset_t, return a + pointer to the first register. */ +#define ALPHA_REGSET_BASE(regsetp) ((long *) (regsetp)) + +extern int kernel_debugging; +extern int kernel_writablecore; + +#define ADDITIONAL_OPTIONS \ + {"kernel", no_argument, &kernel_debugging, 1}, \ + {"k", no_argument, &kernel_debugging, 1}, \ + {"wcore", no_argument, &kernel_writablecore, 1}, \ + {"w", no_argument, &kernel_writablecore, 1}, + +#define ADDITIONAL_OPTION_HELP \ + "\ + --kernel Enable kernel debugging.\n\ + --wcore Make core file writable (only works for /dev/mem).\n\ + This option only works while debugging a kernel !!\n\ +" + +#define DEFAULT_PROMPT kernel_debugging?"(kgdb) ":"(gdb) " + +/* misuse START_PROGRESS to test whether we're running as kgdb */ +/* START_PROGRESS is called at the top of main */ +#undef START_PROGRESS +#define START_PROGRESS(STR,N) \ + if (!strcmp(STR, "kgdb")) \ + kernel_debugging = 1; + diff --git a/gnu/usr.bin/binutils/gdb5/alpha/tm.h b/gnu/usr.bin/binutils/gdb5/alpha/tm.h new file mode 100644 index 0000000..596a0ef02 --- /dev/null +++ b/gnu/usr.bin/binutils/gdb5/alpha/tm.h @@ -0,0 +1,45 @@ +/* $FreeBSD$ */ +/* Definitions to make GDB run on an Alpha box under FreeBSD. The + definitions here are used when the _target_ system is running Linux. + Copyright 1996 Free Software Foundation, Inc. + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 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. */ + +#ifndef TM_FREEBSDALPHA_H +#define TM_FREEBSDALPHA_H + +#include "alpha/tm-alpha.h" +#ifndef S0_REGNUM +#define S0_REGNUM (T7_REGNUM+1) +#endif + + +/* Number of traps that happen between exec'ing the shell to run an + inferior, and when we finally get to the inferior code. This is 2 + on FreeBSD and most implementations. */ + +#undef START_INFERIOR_TRAPS_EXPECTED +#define START_INFERIOR_TRAPS_EXPECTED 2 + +struct objfile; +void freebsd_uthread_new_objfile PARAMS ((struct objfile *objfile)); +#define target_new_objfile(OBJFILE) freebsd_uthread_new_objfile (OBJFILE) + +extern char *freebsd_uthread_pid_to_str PARAMS ((int pid)); +#define target_pid_to_str(PID) freebsd_uthread_pid_to_str (PID) + +#endif /* TM_FREEBSDALPHA_H */ diff --git a/gnu/usr.bin/binutils/gdb5/alpha/version.c b/gnu/usr.bin/binutils/gdb5/alpha/version.c new file mode 100644 index 0000000..84b5d88 --- /dev/null +++ b/gnu/usr.bin/binutils/gdb5/alpha/version.c @@ -0,0 +1,5 @@ +/* $FreeBSD$ */ + +char *version = "4.18"; +char *host_name = "alpha-unknown-freebsd"; +char *target_name = "alpha-unknown-freebsd"; diff --git a/gnu/usr.bin/binutils/gdb5/alpha/xm.h b/gnu/usr.bin/binutils/gdb5/alpha/xm.h new file mode 100644 index 0000000..1bd0118 --- /dev/null +++ b/gnu/usr.bin/binutils/gdb5/alpha/xm.h @@ -0,0 +1,32 @@ +/* Host definitions for GDB running on an Alpha under FreeBSD + Copyright (C) 1996 Free Software Foundation, Inc. + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* $FreeBSD$ */ + +#if !defined (HOST_BYTE_ORDER) +#define HOST_BYTE_ORDER LITTLE_ENDIAN +#endif + +/* The alpha has no siginterrupt routine. */ +#define NO_SIGINTERRUPT + +#define HAVE_TERMIOS +#define HAVE_SIGSETMASK 1 + +#include <limits.h> diff --git a/gnu/usr.bin/binutils/gdb5/config.h b/gnu/usr.bin/binutils/gdb5/config.h new file mode 100644 index 0000000..eb6ff3c --- /dev/null +++ b/gnu/usr.bin/binutils/gdb5/config.h @@ -0,0 +1,328 @@ +/* $FreeBSD$ */ + +/* config.h. Generated automatically by configure. */ +/* config.in. Generated automatically from configure.in by autoheader. */ + +/* Whether malloc must be declared even if <stdlib.h> is included. */ +/* #undef NEED_DECLARATION_MALLOC */ + +/* Whether realloc must be declared even if <stdlib.h> is included. */ +/* #undef NEED_DECLARATION_REALLOC */ + +/* Whether free must be declared even if <stdlib.h> is included. */ +/* #undef NEED_DECLARATION_FREE */ + +/* Whether strerror must be declared even if <string.h> is included. */ +/* #undef NEED_DECLARATION_STRERROR */ + +/* Define if on AIX 3. + System headers sometimes define this. + We just want to avoid a redefinition error message. */ +#ifndef _ALL_SOURCE +/* #undef _ALL_SOURCE */ +#endif + +/* Define if using alloca.c. */ +/* #undef C_ALLOCA */ + +/* Define to empty if the keyword does not work. */ +/* #undef const */ + +/* Define to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems. + This function is required for alloca.c support on those systems. */ +/* #undef CRAY_STACKSEG_END */ + +/* Define if you have alloca, as a function or macro. */ +#define HAVE_ALLOCA 1 + +/* Define if you have <alloca.h> and it should be used (not on Ultrix). */ +/* #undef HAVE_ALLOCA_H */ + +/* Define if the `long double' type works. */ +#define HAVE_LONG_DOUBLE 1 + +/* Define if you have a working `mmap' system call. */ +#define HAVE_MMAP 1 + +/* Define as __inline if that's what the C compiler calls it. */ +/* #undef inline */ + +/* Define to `long' if <sys/types.h> doesn't define. */ +/* #undef off_t */ + +/* Define if you need to in order for stat and other things to work. */ +/* #undef _POSIX_SOURCE */ + +/* Define as the return type of signal handlers (int or void). */ +#define RETSIGTYPE void + +/* Define to `unsigned' if <sys/types.h> doesn't define. */ +/* #undef size_t */ + +/* If using the C implementation of alloca, define if you know the + direction of stack growth for your system; otherwise it will be + automatically deduced at run-time. + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => direction of growth unknown + */ +/* #undef STACK_DIRECTION */ + +/* Define if the `S_IS*' macros in <sys/stat.h> do not work properly. */ +/* #undef STAT_MACROS_BROKEN */ + +/* Define if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define if ioctl argument PIOCSET is available. */ +/* #undef HAVE_PROCFS_PIOCSET */ + +/* /proc PID entries are directories containing the files + ctl as map status */ +/* #undef HAVE_MULTIPLE_PROC_FDS */ + +/* Define if the `long long' type works. */ +#define CC_HAS_LONG_LONG 1 + +/* Define if the "ll" format works to print long long ints. */ +#define PRINTF_HAS_LONG_LONG 1 + +/* Define if the "%Lg" format works to print long doubles. */ +#define PRINTF_HAS_LONG_DOUBLE 1 + +/* Define if the "%Lg" format works to scan long doubles. */ +#define SCANF_HAS_LONG_DOUBLE 1 + +/* Define if using Solaris thread debugging. */ +/* #undef HAVE_THREAD_DB_LIB */ + +/* Define on a GNU/Linux system to work around problems in sys/procfs.h. */ +/* #undef START_INFERIOR_TRAPS_EXPECTED */ +/* #undef sys_quotactl */ + +/* Define if you have HPUX threads */ +/* #undef HAVE_HPUX_THREAD_SUPPORT */ + +/* Define if you want to use the memory mapped malloc package (mmalloc). */ +/* #undef USE_MMALLOC */ + +/* Define if the runtime uses a routine from mmalloc before gdb has a chance + to initialize mmalloc, and we want to force checking to be used anyway. + This may cause spurious memory corruption messages if the runtime tries + to explicitly deallocate that memory when gdb calls exit. */ +/* #undef MMCHECK_FORCE */ + +/* Define if you want to use the full-screen terminal user interface. */ +/* #undef TUI */ + +/* Define if <proc_service.h> on solaris uses int instead of + size_t, and assorted other type changes. */ +/* #undef PROC_SERVICE_IS_OLD */ + +/* Set to true if the save_state_t structure is present */ +#define HAVE_STRUCT_SAVE_STATE_T 0 + +/* Set to true if the save_state_t structure has the ss_wide member */ +#define HAVE_STRUCT_MEMBER_SS_WIDE 0 + +/* Define if you have the __argz_count function. */ +/* #undef HAVE___ARGZ_COUNT */ + +/* Define if you have the __argz_next function. */ +/* #undef HAVE___ARGZ_NEXT */ + +/* Define if you have the __argz_stringify function. */ +/* #undef HAVE___ARGZ_STRINGIFY */ + +/* Define if you have the bcopy function. */ +#define HAVE_BCOPY 1 + +/* Define if you have the btowc function. */ +/* #undef HAVE_BTOWC */ + +/* Define if you have the bzero function. */ +#define HAVE_BZERO 1 + +/* Define if you have the dcgettext function. */ +/* #undef HAVE_DCGETTEXT */ + +/* Define if you have the getcwd function. */ +#define HAVE_GETCWD 1 + +/* Define if you have the getpagesize function. */ +#define HAVE_GETPAGESIZE 1 + +/* Define if you have the isascii function. */ +#define HAVE_ISASCII 1 + +/* Define if you have the munmap function. */ +#define HAVE_MUNMAP 1 + +/* Define if you have the putenv function. */ +#define HAVE_PUTENV 1 + +/* Define if you have the sbrk function. */ +#define HAVE_SBRK 1 + +/* Define if you have the setenv function. */ +#define HAVE_SETENV 1 + +/* Define if you have the setlocale function. */ +#define HAVE_SETLOCALE 1 + +/* Define if you have the setpgid function. */ +#define HAVE_SETPGID 1 + +/* Define if you have the sigaction function. */ +#define HAVE_SIGACTION 1 + +/* Define if you have the stpcpy function. */ +/* #undef HAVE_STPCPY */ + +/* Define if you have the strcasecmp function. */ +#define HAVE_STRCASECMP 1 + +/* Define if you have the strchr function. */ +#define HAVE_STRCHR 1 + +/* Define if you have the <argz.h> header file. */ +/* #undef HAVE_ARGZ_H */ + +/* Define if you have the <asm/debugreg.h> header file. */ +/* #undef HAVE_ASM_DEBUGREG_H */ + +/* Define if you have the <ctype.h> header file. */ +#define HAVE_CTYPE_H 1 + +/* Define if you have the <curses.h> header file. */ +#define HAVE_CURSES_H 1 + +/* Define if you have the <endian.h> header file. */ +/* #undef HAVE_ENDIAN_H */ + +/* Define if you have the <limits.h> header file. */ +#define HAVE_LIMITS_H 1 + +/* Define if you have the <link.h> header file. */ +#define HAVE_LINK_H 1 + +/* Define if you have the <locale.h> header file. */ +#define HAVE_LOCALE_H 1 + +/* Define if you have the <malloc.h> header file. */ +/* #undef HAVE_MALLOC_H */ + +/* Define if you have the <memory.h> header file. */ +#define HAVE_MEMORY_H 1 + +/* Define if you have the <nl_types.h> header file. */ +#define HAVE_NL_TYPES_H 1 + +/* Define if you have the <objlist.h> header file. */ +/* #undef HAVE_OBJLIST_H */ + +/* Define if you have the <ptrace.h> header file. */ +/* #undef HAVE_PTRACE_H */ + +/* Define if you have the <sgtty.h> header file. */ +#define HAVE_SGTTY_H 1 + +/* Define if you have the <stddef.h> header file. */ +#define HAVE_STDDEF_H 1 + +/* Define if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define if you have the <sys/debugreg.h> header file. */ +/* #undef HAVE_SYS_DEBUGREG_H */ + +/* Define if you have the <sys/param.h> header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define if you have the <sys/procfs.h> header file. */ +#define HAVE_SYS_PROCFS_H 1 + +/* Define if you have the <sys/ptrace.h> header file. */ +#define HAVE_SYS_PTRACE_H 1 + +/* Define if you have the <sys/reg.h> header file. */ +/* #undef HAVE_SYS_REG_H */ + +/* Define if you have the <sys/wait.h> header file. */ +#define HAVE_SYS_WAIT_H 1 + +/* Define if you have the <term.h> header file. */ +#define HAVE_TERM_H 1 + +/* Define if you have the <termio.h> header file. */ +/* #undef HAVE_TERMIO_H */ + +/* Define if you have the <termios.h> header file. */ +#define HAVE_TERMIOS_H 1 + +/* Define if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Define if you have the <values.h> header file. */ +/* #undef HAVE_VALUES_H */ + +/* Define if you have the <wait.h> header file. */ +/* #undef HAVE_WAIT_H */ + +/* Define if you have the <wchar.h> header file. */ +/* #undef HAVE_WCHAR_H */ + +/* Define if you have the <wctype.h> header file. */ +/* #undef HAVE_WCTYPE_H */ + +/* Define if you have the dl library (-ldl). */ +/* #undef HAVE_LIBDL */ + +/* Define if you have the m library (-lm). */ +#define HAVE_LIBM 1 + +/* Define if you have the w library (-lw). */ +/* #undef HAVE_LIBW */ + +/* Define if you have the stpcpy function */ +/* #undef HAVE_STPCPY */ + +/* Define if your locale.h file contains LC_MESSAGES. */ +#define HAVE_LC_MESSAGES 1 + +/* Define to 1 if NLS is requested */ +#define ENABLE_NLS 1 + +/* Define as 1 if you have gettext and don't want to use GNU gettext. */ +/* #undef HAVE_GETTEXT */ + +/* Define if malloc is not declared in system header files. */ +/* #undef NEED_DECLARATION_MALLOC */ + +/* Define if realloc is not declared in system header files. */ +/* #undef NEED_DECLARATION_REALLOC */ + +/* Define if free is not declared in system header files. */ +/* #undef NEED_DECLARATION_FREE */ + +/* Define if strerror is not declared in system header files. */ +/* #undef NEED_DECLARATION_STRERROR */ + +/* Define if strdup is not declared in system header files. */ +/* #undef NEED_DECLARATION_STRDUP */ + +/* Define if <sys/procfs.h> has pstatus_t. */ +/* #undef HAVE_PSTATUS_T */ + +/* Define if <sys/procfs.h> has prrun_t. */ +/* #undef HAVE_PRRUN_T */ + +/* Define if <sys/procfs.h> has gregset_t. */ +#define HAVE_GREGSET_T 1 + +/* Define if <sys/procfs.h> has fpregset_t. */ +#define HAVE_FPREGSET_T 1 + diff --git a/gnu/usr.bin/binutils/gdb5/freebsd-uthread.c b/gnu/usr.bin/binutils/gdb5/freebsd-uthread.c new file mode 100644 index 0000000..1c1ea78 --- /dev/null +++ b/gnu/usr.bin/binutils/gdb5/freebsd-uthread.c @@ -0,0 +1,1139 @@ +/* $FreeBSD$ */ +/* Low level interface for debugging FreeBSD user threads for GDB, the GNU debugger. + Copyright 1996, 1999 Free Software Foundation, Inc. + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 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. */ + +/* This module implements a sort of half target that sits between the + machine-independent parts of GDB and the ptrace interface (infptrace.c) to + provide access to the FreeBSD user-mode thread implementation. + + FreeBSD threads are true user-mode threads, which are invoked via + the pthread_* interfaces. These are mostly implemented in + user-space, with all thread context kept in various structures that + live in the user's heap. For the most part, the kernel has no + knowlege of these threads. + + Based largely on hpux-thread.c + + */ + + +#include "defs.h" +#include <sys/queue.h> +#include <signal.h> +#include <setjmp.h> +#include "gdbthread.h" +#include "target.h" +#include "inferior.h" +#include <fcntl.h> +#include <unistd.h> +#include <sys/stat.h> +#include "gdbcore.h" + +extern int child_suppress_run; +extern struct target_ops child_ops; /* target vector for inftarg.c */ + +extern void _initialize_freebsd_uthread PARAMS ((void)); + +static int main_pid = -1; /* Real process ID */ + +/* Set to true while we are part-way through attaching */ +static int freebsd_uthread_attaching; + +static int freebsd_uthread_active = 0; +static CORE_ADDR P_thread_list; +static CORE_ADDR P_thread_run; + +static struct cleanup * save_inferior_pid PARAMS ((void)); + +static void restore_inferior_pid PARAMS ((int pid)); + +static void freebsd_uthread_resume PARAMS ((int pid, int step, + enum target_signal signo)); + +static void init_freebsd_uthread_ops PARAMS ((void)); + +static struct target_ops freebsd_uthread_ops; +static struct target_thread_vector freebsd_uthread_vec; + +/* + +LOCAL FUNCTION + + save_inferior_pid - Save inferior_pid on the cleanup list + restore_inferior_pid - Restore inferior_pid from the cleanup list + +SYNOPSIS + + struct cleanup *save_inferior_pid () + void restore_inferior_pid (int pid) + +DESCRIPTION + + These two functions act in unison to restore inferior_pid in + case of an error. + +NOTES + + inferior_pid is a global variable that needs to be changed by many of + these routines before calling functions in procfs.c. In order to + guarantee that inferior_pid gets restored (in case of errors), you + need to call save_inferior_pid before changing it. At the end of the + function, you should invoke do_cleanups to restore it. + + */ + +static struct cleanup * +save_inferior_pid () +{ + return make_cleanup ((make_cleanup_func) restore_inferior_pid, + (void *)(intptr_t) inferior_pid); +} + +static void +restore_inferior_pid (pid) + int pid; +{ + inferior_pid = pid; +} + +static int find_active_thread PARAMS ((void)); + +struct cached_pthread { + u_int64_t uniqueid; + int state; + CORE_ADDR name; + int ctxtype; + union { + ucontext_t uc; + jmp_buf jb; + } ctx; +}; + +static int cached_thread; +static struct cached_pthread cached_pthread; +static CORE_ADDR cached_pthread_addr; + +#define THREADID_TID(id) ((id) >> 17) +#define THREADID_PID(id) ((id) & ((1 << 17) - 1)) + +LIST_HEAD(idmaplist, idmap); + +struct idmap { + LIST_ENTRY(idmap) link; + u_int64_t uniqueid; + int tid; +}; + +#define MAPHASH_SIZE 257 +#define TID_MIN 1 +#define TID_MAX 16383 + +static int tid_to_hash[TID_MAX + 1]; /* set to map_hash index */ +static struct idmaplist map_hash[MAPHASH_SIZE]; +static int next_free_tid = TID_MIN; /* first available tid */ +static int last_free_tid = TID_MIN; /* first unavailable */ + +static CORE_ADDR P_thread_next_offset; +static CORE_ADDR P_thread_uniqueid_offset; +static CORE_ADDR P_thread_state_offset; +static CORE_ADDR P_thread_name_offset; +static CORE_ADDR P_thread_ctxtype_offset; +static CORE_ADDR P_thread_ctx_offset; +static CORE_ADDR P_thread_PS_RUNNING_value; +static CORE_ADDR P_thread_PS_DEAD_value; +static CORE_ADDR P_thread_CTX_JB_NOSIG_value; +static CORE_ADDR P_thread_CTX_JB_value; +static CORE_ADDR P_thread_CTX_SJB_value; +static CORE_ADDR P_thread_CTX_UC_value; + +static int next_offset; +static int uniqueid_offset; +static int state_offset; +static int name_offset; +static int ctxtype_offset; +static int ctx_offset; +static int PS_RUNNING_value; +static int PS_DEAD_value; +static int CTX_JB_NOSIG_value; +static int CTX_JB_value; +static int CTX_SJB_value; +static int CTX_UC_value; + +#define UNIQUEID_HASH(id) (id % MAPHASH_SIZE) +#define TID_ADD1(tid) (((tid) + 1) == TID_MAX + 1 \ + ? TID_MIN : (tid) + 1) +#define IS_TID_FREE(tid) (tid_to_hash[tid] == -1) + +static int +get_new_tid(h) + int h; +{ + int tid = next_free_tid; + + tid_to_hash[tid] = h; + next_free_tid = TID_ADD1(next_free_tid); + if (next_free_tid == last_free_tid) + { + int i; + + for (i = last_free_tid; TID_ADD1(i) != last_free_tid; i = TID_ADD1(i)) + if (IS_TID_FREE(i)) + break; + if (TID_ADD1(i) == last_free_tid) + { + error("too many threads"); + return 0; + } + next_free_tid = i; + for (i = TID_ADD1(i); IS_TID_FREE(i); i = TID_ADD1(i)) + ; + last_free_tid = i; + } + + return tid; +} + +static int +find_pid(uniqueid) + u_int64_t uniqueid; +{ + int h = UNIQUEID_HASH(uniqueid); + struct idmap *im; + + LIST_FOREACH(im, &map_hash[h], link) + if (im->uniqueid == uniqueid) + return (im->tid << 17) + main_pid; + + im = xmalloc(sizeof(struct idmap)); + im->uniqueid = uniqueid; + im->tid = get_new_tid(h); + LIST_INSERT_HEAD(&map_hash[h], im, link); + + return (im->tid << 17) + main_pid; +} + +static void +free_pid(pid) + int pid; +{ + int tid = THREADID_TID(pid); + int h = tid_to_hash[tid]; + struct idmap *im; + + if (!tid) return; + + LIST_FOREACH(im, &map_hash[h], link) + if (im->tid == tid) + break; + + if (!im) return; + + LIST_REMOVE(im, link); + tid_to_hash[tid] = -1; + free(im); +} + +#define READ_OFFSET(field) read_memory(P_thread_##field##_offset, \ + (char *) &field##_offset, \ + sizeof(field##_offset)) + +#define READ_VALUE(name) read_memory(P_thread_##name##_value, \ + (char *) &name##_value, \ + sizeof(name##_value)) + +static void +read_thread_offsets () +{ + READ_OFFSET(next); + READ_OFFSET(uniqueid); + READ_OFFSET(state); + READ_OFFSET(name); + READ_OFFSET(ctxtype); + READ_OFFSET(ctx); + + READ_VALUE(PS_RUNNING); + READ_VALUE(PS_DEAD); + READ_VALUE(CTX_JB_NOSIG); + READ_VALUE(CTX_JB); + READ_VALUE(CTX_SJB); + READ_VALUE(CTX_UC); +} + +#define READ_FIELD(ptr, T, field, result) \ + read_memory ((ptr) + field##_offset, (char *) &(result), sizeof result) + +static u_int64_t +read_pthread_uniqueid (ptr) + CORE_ADDR ptr; +{ + u_int64_t uniqueid; + READ_FIELD(ptr, u_int64_t, uniqueid, uniqueid); + return uniqueid; +} + +static CORE_ADDR +read_pthread_next (ptr) + CORE_ADDR ptr; +{ + CORE_ADDR next; + READ_FIELD(ptr, CORE_ADDR, next, next); + return next; +} + +static void +read_cached_pthread (ptr, cache) + CORE_ADDR ptr; + struct cached_pthread *cache; +{ + READ_FIELD(ptr, u_int64_t, uniqueid, cache->uniqueid); + READ_FIELD(ptr, int, state, cache->state); + READ_FIELD(ptr, CORE_ADDR, name, cache->name); + READ_FIELD(ptr, int, ctxtype, cache->ctxtype); + READ_FIELD(ptr, ucontext_t, ctx, cache->ctx); +} + +static int +find_active_thread () +{ + CORE_ADDR ptr; + + if (main_pid == -1) + return -1; + + read_memory ((CORE_ADDR)P_thread_run, + (char *)&ptr, + sizeof ptr); + + return find_pid(read_pthread_uniqueid(ptr)); +} + +static CORE_ADDR find_pthread_addr PARAMS ((int thread)); +static struct cached_pthread * find_pthread PARAMS ((int thread)); + +static CORE_ADDR +find_pthread_addr (thread) + int thread; +{ + CORE_ADDR ptr; + + if (thread == cached_thread) + return cached_pthread_addr; + + read_memory ((CORE_ADDR)P_thread_list, + (char *)&ptr, + sizeof ptr); + + while (ptr != 0) + { + if (find_pid(read_pthread_uniqueid(ptr)) == thread) + { + cached_thread = thread; + cached_pthread_addr = ptr; + read_cached_pthread(ptr, &cached_pthread); + return ptr; + } + ptr = read_pthread_next(ptr); + } + + return NULL; +} + +static struct cached_pthread * +find_pthread (thread) + int thread; +{ + CORE_ADDR ptr; + + if (thread == cached_thread) + return &cached_pthread; + + read_memory ((CORE_ADDR)P_thread_list, + (char *)&ptr, + sizeof ptr); + + while (ptr != 0) + { + if (find_pid(read_pthread_uniqueid(ptr)) == thread) + { + cached_thread = thread; + cached_pthread_addr = ptr; + read_cached_pthread(ptr, &cached_pthread); + return &cached_pthread; + } + ptr = read_pthread_next(ptr); + } + +#if 0 + error ("Can't find pthread %d,%d", + THREADID_TID(thread), THREADID_PID(thread)); +#endif + return NULL; +} + + +/* Most target vector functions from here on actually just pass through to + inftarg.c, as they don't need to do anything specific for threads. */ + +/* ARGSUSED */ +static void +freebsd_uthread_open (arg, from_tty) + char *arg; + int from_tty; +{ + child_ops.to_open (arg, from_tty); +} + +/* Attach to process PID, then initialize for debugging it + and wait for the trace-trap that results from attaching. */ + +static void +freebsd_uthread_attach (args, from_tty) + char *args; + int from_tty; +{ + child_ops.to_attach (args, from_tty); + push_target (&freebsd_uthread_ops); + freebsd_uthread_attaching = 1; +} + +/* After an attach, see if the target is threaded */ + +static void +freebsd_uthread_post_attach (pid) + int pid; +{ + if (freebsd_uthread_active) + { + read_thread_offsets (); + + main_pid = pid; + + bind_target_thread_vector (&freebsd_uthread_vec); + + inferior_pid = find_active_thread (); + + add_thread (inferior_pid); + } + else + { + unpush_target (&freebsd_uthread_ops); + push_target (&child_ops); + } + + freebsd_uthread_attaching = 0; +} + +/* Take a program previously attached to and detaches it. + The program resumes execution and will no longer stop + on signals, etc. We'd better not have left any breakpoints + in the program or it'll die when it hits one. For this + to work, it may be necessary for the process to have been + previously attached. It *might* work if the program was + started via the normal ptrace (PTRACE_TRACEME). */ + +static void +freebsd_uthread_detach (args, from_tty) + char *args; + int from_tty; +{ + child_ops.to_detach (args, from_tty); +} + +/* Resume execution of process PID. If STEP is nozero, then + just single step it. If SIGNAL is nonzero, restart it with that + signal activated. We may have to convert pid from a thread-id to an LWP id + for procfs. */ + +static void +freebsd_uthread_resume (pid, step, signo) + int pid; + int step; + enum target_signal signo; +{ + struct cleanup *old_chain; + + if (freebsd_uthread_attaching) + { + child_ops.to_resume (pid, step, signo); + return; + } + + old_chain = save_inferior_pid (); + + pid = inferior_pid = main_pid; + + child_ops.to_resume (pid, step, signo); + + cached_thread = 0; + + do_cleanups (old_chain); +} + +/* Wait for any threads to stop. We may have to convert PID from a thread id + to a LWP id, and vice versa on the way out. */ + +static int +freebsd_uthread_wait (pid, ourstatus) + int pid; + struct target_waitstatus *ourstatus; +{ + int rtnval; + struct cleanup *old_chain; + + if (freebsd_uthread_attaching) + { + return child_ops.to_wait (pid, ourstatus); + } + + old_chain = save_inferior_pid (); + + inferior_pid = main_pid; + + if (pid != -1) + pid = main_pid; + + rtnval = child_ops.to_wait (pid, ourstatus); + + if (rtnval >= 0) + { + rtnval = find_active_thread (); + if (!in_thread_list (rtnval)) + add_thread (rtnval); + } + + do_cleanups (old_chain); + + return rtnval; +} + +#ifdef __i386__ + +static char sigmap[NUM_REGS] = /* map reg to sigcontext */ +{ + 12, /* eax */ + 11, /* ecx */ + 10, /* edx */ + 9, /* ebx */ + 8, /* esp */ + 7, /* ebp */ + 6, /* esi */ + 5, /* edi */ + 15, /* eip */ + 17, /* eflags */ + 16, /* cs */ + 19, /* ss */ + 4, /* ds */ + 3, /* es */ + 2, /* fs */ + 1, /* gs */ +}; + +static char jmpmap[NUM_REGS] = /* map reg to jmp_buf */ +{ + 6, /* eax */ + -1, /* ecx */ + -1, /* edx */ + 1, /* ebx */ + 2, /* esp */ + 3, /* ebp */ + 4, /* esi */ + 5, /* edi */ + 0, /* eip */ + -1, /* eflags */ + -1, /* cs */ + -1, /* ss */ + -1, /* ds */ + -1, /* es */ + -1, /* fs */ + -1, /* gs */ +}; + +#endif + +#ifdef __alpha__ + +static char sigmap[NUM_REGS] = /* map reg to sigcontext */ +{ + 1, 2, 3, 4, 5, 6, 7, 8, /* v0 - t6 */ + 9, 10, 11, 12, 13, 14, 15, 16, /* t7 - fp */ + 17, 18, 19, 20, 21, 22, 23, 24, /* a0 - t9 */ + 25, 26, 27, 28, 29, 30, 31, 32, /* t10 - zero */ + 38, 39, 40, 41, 42, 43, 44, 45, /* f0 - f7 */ + 46, 47, 48, 49, 50, 51, 52, 53, /* f8 - f15 */ + 54, 55, 56, 57, 58, 59, 60, 61, /* f16 - f23 */ + 62, 63, 64, 65, 66, 67, 68, 69, /* f24 - f31 */ + 33, -1 /* pc, vfp */ +}; +static char jmpmap[NUM_REGS] = { + 4, 5, 6, 7, 8, 9, 10, 11, /* v0 - t6 */ + 12, 13, 14, 15, 16, 17, 18, 19, /* t7 - fp */ + 20, 21, 22, 23, 24, 25, 26, 27, /* a0 - t9 */ + 28, 29, 30, 31, 32, 33, 34, 35, /* t10 - zero */ + 37, 38, 39, 40, 41, 42, 43, 44, /* f0 - f7 */ + 45, 46, 47, 48, 49, 50, 51, 52, /* f8 - f15 */ + 53, 54, 55, 56, 57, 58, 59, 60, /* f16 - f23 */ + 61, 62, 63, 64, 65, 66, 67, 68, /* f24 - f31 */ + 2, -1, /* pc, vfp */ +}; + +#endif + +static void +freebsd_uthread_fetch_registers (regno) + int regno; +{ + struct cached_pthread *thread; + struct cleanup *old_chain; + int active; + int first_regno, last_regno; + register_t *regbase; + char *regmap; + + if (freebsd_uthread_attaching) + { + child_ops.to_fetch_registers (regno); + return; + } + + thread = find_pthread (inferior_pid); + + old_chain = save_inferior_pid (); + + active = (inferior_pid == find_active_thread()); + + inferior_pid = main_pid; + + if (active) + { + child_ops.to_fetch_registers (regno); + + do_cleanups (old_chain); + + return; + } + + if (regno == -1) + { + first_regno = 0; + last_regno = NUM_REGS - 1; + } + else + { + first_regno = regno; + last_regno = regno; + } + + if (thread->ctxtype == CTX_UC_value) + { + regbase = (register_t*) &thread->ctx.uc.uc_mcontext; + regmap = sigmap; + } + else + { + regbase = (register_t*) &thread->ctx.jb[0]; + regmap = jmpmap; + } + + for (regno = first_regno; regno <= last_regno; regno++) + { + if (regmap[regno] == -1) + child_ops.to_fetch_registers (regno); + else + supply_register (regno, (char*) ®base[regmap[regno]]); + } + + do_cleanups (old_chain); +} + +static void +freebsd_uthread_store_registers (regno) + int regno; +{ + struct cached_pthread *thread; + CORE_ADDR ptr; + struct cleanup *old_chain; + int first_regno, last_regno; + u_int32_t *regbase; + char *regmap; + + if (freebsd_uthread_attaching) + { + child_ops.to_store_registers (regno); + return; + } + + thread = find_pthread (inferior_pid); + + old_chain = save_inferior_pid (); + + inferior_pid = main_pid; + + if (thread->state == PS_RUNNING_value) + { + child_ops.to_store_registers (regno); + + do_cleanups (old_chain); + + return; + } + + if (regno == -1) + { + first_regno = 0; + last_regno = NUM_REGS - 1; + } + else + { + first_regno = regno; + last_regno = regno; + } + + if (thread->ctxtype == CTX_UC_value) + { + regbase = (u_int32_t*) &thread->ctx.uc.uc_mcontext; + regmap = sigmap; + } + else + { + regbase = (u_int32_t*) &thread->ctx.jb[0]; + regmap = jmpmap; + } + + ptr = find_pthread_addr (inferior_pid); + for (regno = first_regno; regno <= last_regno; regno++) + { + if (regmap[regno] == -1) + child_ops.to_store_registers (regno); + else + { + u_int32_t *reg = ®base[regmap[regno]]; + int off; + + /* Hang onto cached value */ + memcpy(reg, registers + REGISTER_BYTE (regno), + REGISTER_RAW_SIZE (regno)); + + /* And push out to inferior */ + off = (char *) reg - (char *) thread; + write_memory (ptr + off, + registers + REGISTER_BYTE (regno), + REGISTER_RAW_SIZE (regno)); + } + } + + do_cleanups (old_chain); +} + +/* Get ready to modify the registers array. On machines which store + individual registers, this doesn't need to do anything. On machines + which store all the registers in one fell swoop, this makes sure + that registers contains all the registers from the program being + debugged. */ + +static void +freebsd_uthread_prepare_to_store () +{ + struct cleanup *old_chain; + + if (freebsd_uthread_attaching) + { + child_ops.to_prepare_to_store (); + return; + } + + old_chain = save_inferior_pid (); + inferior_pid = main_pid; + + child_ops.to_prepare_to_store (); + + do_cleanups (old_chain); +} + +static int +freebsd_uthread_xfer_memory (memaddr, myaddr, len, dowrite, target) + CORE_ADDR memaddr; + char *myaddr; + int len; + int dowrite; + struct target_ops *target; /* ignored */ +{ + int retval; + struct cleanup *old_chain; + + if (freebsd_uthread_attaching) + { + return child_ops.to_xfer_memory (memaddr, myaddr, len, dowrite, target); + } + + old_chain = save_inferior_pid (); + + inferior_pid = main_pid; + + retval = child_ops.to_xfer_memory (memaddr, myaddr, len, dowrite, target); + + do_cleanups (old_chain); + + return retval; +} + +/* Print status information about what we're accessing. */ + +static void +freebsd_uthread_files_info (ignore) + struct target_ops *ignore; +{ + child_ops.to_files_info (ignore); +} + +static void +freebsd_uthread_kill_inferior () +{ + inferior_pid = main_pid; + child_ops.to_kill (); +} + +static void +freebsd_uthread_notice_signals (pid) + int pid; +{ + struct cleanup *old_chain; + old_chain = save_inferior_pid (); + inferior_pid = main_pid; + + child_ops.to_notice_signals (pid); + + do_cleanups (old_chain); +} + +/* Fork an inferior process, and start debugging it with /proc. */ + +static void +freebsd_uthread_create_inferior (exec_file, allargs, env) + char *exec_file; + char *allargs; + char **env; +{ + child_ops.to_create_inferior (exec_file, allargs, env); + + if (inferior_pid && freebsd_uthread_active) + { + read_thread_offsets (); + + main_pid = inferior_pid; + + push_target (&freebsd_uthread_ops); + bind_target_thread_vector (&freebsd_uthread_vec); + + inferior_pid = find_active_thread (); + + add_thread (inferior_pid); + } +} + +/* This routine is called to find out if the inferior is using threads. + We check for the _thread_run and _thread_list globals. */ + +void +freebsd_uthread_new_objfile (objfile) + struct objfile *objfile; +{ + struct minimal_symbol *ms; + + if (!objfile) + { + freebsd_uthread_active = 0; + return; + } + + ms = lookup_minimal_symbol ("_thread_run", NULL, objfile); + + if (!ms) + return; + + P_thread_run = SYMBOL_VALUE_ADDRESS (ms); + + ms = lookup_minimal_symbol ("_thread_list", NULL, objfile); + + if (!ms) + return; + + P_thread_list = SYMBOL_VALUE_ADDRESS (ms); + +#define OFFSET_SYM(field) "_thread_" #field "_offset" +#define LOOKUP_OFFSET(field) \ + do { \ + ms = lookup_minimal_symbol (OFFSET_SYM(field), NULL, objfile); \ + if (!ms) \ + return; \ + P_thread_##field##_offset = SYMBOL_VALUE_ADDRESS (ms); \ + } while (0); + +#define VALUE_SYM(name) "_thread_" #name "_value" +#define LOOKUP_VALUE(name) \ + do { \ + ms = lookup_minimal_symbol (VALUE_SYM(name), NULL, objfile); \ + if (!ms) \ + return; \ + P_thread_##name##_value = SYMBOL_VALUE_ADDRESS (ms); \ + } while (0); + + LOOKUP_OFFSET(next); + LOOKUP_OFFSET(uniqueid); + LOOKUP_OFFSET(state); + LOOKUP_OFFSET(name); + LOOKUP_OFFSET(ctxtype); + LOOKUP_OFFSET(ctx); + + LOOKUP_VALUE(PS_RUNNING); + LOOKUP_VALUE(PS_DEAD); + LOOKUP_VALUE(CTX_JB_NOSIG); + LOOKUP_VALUE(CTX_JB); + LOOKUP_VALUE(CTX_SJB); + LOOKUP_VALUE(CTX_UC); + + freebsd_uthread_active = 1; +} + +int +freebsd_uthread_has_exited (pid, wait_status, exit_status) + int pid; + int wait_status; + int * exit_status; +{ + int t = child_ops.to_has_exited (pid, wait_status, exit_status); + if (t) + main_pid = -1; + return t; +} + +/* Clean up after the inferior dies. */ + +static void +freebsd_uthread_mourn_inferior () +{ + inferior_pid = main_pid; /* don't bother to restore inferior_pid */ + child_ops.to_mourn_inferior (); + unpush_target (&freebsd_uthread_ops); +} + +/* Mark our target-struct as eligible for stray "run" and "attach" commands. */ + +static int +freebsd_uthread_can_run () +{ + return child_suppress_run; +} + +static int +freebsd_uthread_thread_alive (pid) + int pid; +{ + struct cleanup *old_chain; + struct cached_pthread *thread; + int ret = 0; + + if (freebsd_uthread_attaching) + return 1; + + /* + * We can get called from child_ops.to_wait() which passes the underlying + * pid (without a thread number). + */ + if (THREADID_TID(pid) == 0) + return 1; + + old_chain = save_inferior_pid (); + inferior_pid = main_pid; + + if (find_pthread_addr (pid) != 0) + { + thread = find_pthread (pid); + ret = (thread->state != PS_DEAD_value); + } + + do_cleanups (old_chain); + + if (!ret) + free_pid(pid); + + return ret; +} + +static void +freebsd_uthread_stop () +{ + struct cleanup *old_chain; + old_chain = save_inferior_pid (); + inferior_pid = main_pid; + + child_ops.to_stop (); + + do_cleanups (old_chain); +} + +static int +freebsd_uthread_find_new_threads () +{ + CORE_ADDR ptr; + int state; + u_int64_t uniqueid; + struct cleanup *old_chain; + + old_chain = save_inferior_pid (); + inferior_pid = main_pid; + + read_memory ((CORE_ADDR)P_thread_list, + (char *)&ptr, + sizeof ptr); + + while (ptr != 0) + { + READ_FIELD(ptr, int, state, state); + READ_FIELD(ptr, u_int64_t, uniqueid, uniqueid); + if (state != PS_DEAD_value && + !in_thread_list (find_pid(uniqueid))) + add_thread (find_pid(uniqueid)); + ptr = read_pthread_next(ptr); + } + + do_cleanups (old_chain); + + return 0; +} + +/* MUST MATCH enum pthread_state */ +static const char *statenames[] = { + "RUNNING", + "SIGTHREAD", + "MUTEX_WAIT", + "COND_WAIT", + "FDLR_WAIT", + "FDLW_WAIT", + "FDR_WAIT", + "FDW_WAIT", + "POLL_WAIT", + "FILE_WAIT", + "SELECT_WAIT", + "SLEEP_WAIT", + "WAIT_WAIT", + "SIGSUSPEND", + "SIGWAIT", + "SPINBLOCK", + "JOIN", + "SUSPENDED", + "DEAD", + "DEADLOCK", +}; + +static int +freebsd_uthread_get_thread_info (ref, selection, info) + gdb_threadref *ref; + int selection; + struct gdb_ext_thread_info *info; +{ + int pid = *ref; + struct cached_pthread *thread = find_pthread (pid); + struct cleanup *old_chain; + + old_chain = save_inferior_pid (); + inferior_pid = main_pid; + + memset(&info->threadid, 0, OPAQUETHREADBYTES); + + memcpy(&info->threadid, ref, sizeof *ref); + info->active = thread->state == PS_RUNNING_value; + strcpy(info->display, statenames[thread->state]); + if (thread->name) + read_memory ((CORE_ADDR) thread->name, info->shortname, 32); + else + strcpy(info->shortname, ""); + + do_cleanups (old_chain); + return (0); +} + +char * +freebsd_uthread_pid_to_str (pid) + int pid; +{ + static char buf[30]; + + if (STREQ (current_target.to_shortname, "freebsd-uthreads")) + sprintf (buf, "process %d, thread %d\0", + THREADID_PID(pid), THREADID_TID(pid)); + else + sprintf (buf, "process %d\0", pid); + + return buf; +} + + +static void +init_freebsd_uthread_ops () +{ + freebsd_uthread_ops.to_shortname = "freebsd-uthreads"; + freebsd_uthread_ops.to_longname = "FreeBSD uthreads"; + freebsd_uthread_ops.to_doc = "FreeBSD user threads support."; + freebsd_uthread_ops.to_open = freebsd_uthread_open; + freebsd_uthread_ops.to_attach = freebsd_uthread_attach; + freebsd_uthread_ops.to_post_attach = freebsd_uthread_post_attach; + freebsd_uthread_ops.to_detach = freebsd_uthread_detach; + freebsd_uthread_ops.to_resume = freebsd_uthread_resume; + freebsd_uthread_ops.to_wait = freebsd_uthread_wait; + freebsd_uthread_ops.to_fetch_registers = freebsd_uthread_fetch_registers; + freebsd_uthread_ops.to_store_registers = freebsd_uthread_store_registers; + freebsd_uthread_ops.to_prepare_to_store = freebsd_uthread_prepare_to_store; + freebsd_uthread_ops.to_xfer_memory = freebsd_uthread_xfer_memory; + freebsd_uthread_ops.to_files_info = freebsd_uthread_files_info; + freebsd_uthread_ops.to_insert_breakpoint = memory_insert_breakpoint; + freebsd_uthread_ops.to_remove_breakpoint = memory_remove_breakpoint; + freebsd_uthread_ops.to_terminal_init = terminal_init_inferior; + freebsd_uthread_ops.to_terminal_inferior = terminal_inferior; + freebsd_uthread_ops.to_terminal_ours_for_output = terminal_ours_for_output; + freebsd_uthread_ops.to_terminal_ours = terminal_ours; + freebsd_uthread_ops.to_terminal_info = child_terminal_info; + freebsd_uthread_ops.to_kill = freebsd_uthread_kill_inferior; + freebsd_uthread_ops.to_create_inferior = freebsd_uthread_create_inferior; + freebsd_uthread_ops.to_has_exited = freebsd_uthread_has_exited; + freebsd_uthread_ops.to_mourn_inferior = freebsd_uthread_mourn_inferior; + freebsd_uthread_ops.to_can_run = freebsd_uthread_can_run; + freebsd_uthread_ops.to_notice_signals = freebsd_uthread_notice_signals; + freebsd_uthread_ops.to_thread_alive = freebsd_uthread_thread_alive; + freebsd_uthread_ops.to_stop = freebsd_uthread_stop; + freebsd_uthread_ops.to_stratum = process_stratum; + freebsd_uthread_ops.to_has_all_memory = 1; + freebsd_uthread_ops.to_has_memory = 1; + freebsd_uthread_ops.to_has_stack = 1; + freebsd_uthread_ops.to_has_registers = 1; + freebsd_uthread_ops.to_has_execution = 1; + freebsd_uthread_ops.to_has_thread_control = 0; + freebsd_uthread_ops.to_magic = OPS_MAGIC; + + freebsd_uthread_vec.find_new_threads = freebsd_uthread_find_new_threads; + freebsd_uthread_vec.get_thread_info = freebsd_uthread_get_thread_info; +} + +void +_initialize_freebsd_uthread () +{ + init_freebsd_uthread_ops (); + add_target (&freebsd_uthread_ops); + + child_suppress_run = 1; +} diff --git a/gnu/usr.bin/binutils/gdb5/i386/freebsd-nat.c b/gnu/usr.bin/binutils/gdb5/i386/freebsd-nat.c new file mode 100644 index 0000000..aca4b6d --- /dev/null +++ b/gnu/usr.bin/binutils/gdb5/i386/freebsd-nat.c @@ -0,0 +1,670 @@ +/* $FreeBSD$ */ +/* Native-dependent code for BSD Unix running on i386's, for GDB. + Copyright 1988, 1989, 1991, 1992, 1994, 1996 Free Software Foundation, Inc. + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 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. */ + +/* $FreeBSD$ */ + +#include "defs.h" + +#include <sys/types.h> +#include <sys/ptrace.h> +#include <machine/reg.h> +#include <machine/frame.h> +#include <sys/param.h> +#include <sys/user.h> +#include "gdbcore.h" +#include "value.h" +#include "inferior.h" + +#if defined(HAVE_GREGSET_T) || defined(HAVE_FPREGSET_T) +#include <sys/procfs.h> +#endif + +/* this table must line up with REGISTER_NAMES in tm-i386v.h */ +/* symbols like 'tEAX' come from <machine/reg.h> */ +static int tregmap[] = +{ + tEAX, tECX, tEDX, tEBX, + tESP, tEBP, tESI, tEDI, + tEIP, tEFLAGS, tCS, tSS, + tDS, tES, tFS, tGS, +}; + +static struct save87 pcb_savefpu; + +void +fetch_inferior_registers (regno) + int regno; +{ + struct reg inferior_registers; /* ptrace order, not gcc/gdb order */ + int r; + + ptrace (PT_GETREGS, inferior_pid, + (PTRACE_ARG3_TYPE) &inferior_registers, 0); + + for (r = 0; r < NUM_REGS; r++) + memcpy (®isters[REGISTER_BYTE (r)], ((int *)&inferior_registers) + tregmap[r], 4); + + registers_fetched (); +} + +void +store_inferior_registers (regno) + int regno; +{ + struct reg inferior_registers; /* ptrace order, not gcc/gdb order */ + int r; + + for (r = 0; r < NUM_REGS; r++) + memcpy (((int *)&inferior_registers) + tregmap[r], ®isters[REGISTER_BYTE (r)], 4); + + ptrace (PT_SETREGS, inferior_pid, + (PTRACE_ARG3_TYPE) &inferior_registers, 0); +} + +/* Extract the register values out of the core file and store + them where `read_register' will find them. + Extract the floating point state out of the core file and store + it where `float_info' will find it. + + CORE_REG_SECT points to the register values themselves, read into memory. + CORE_REG_SIZE is the size of that area. + WHICH says which set of registers we are handling (0 = int, 2 = float + on machines where they are discontiguous). + REG_ADDR is the offset from u.u_ar0 to the register values relative to + core_reg_sect. This is used with old-fashioned core files to + locate the registers in a large upage-plus-stack ".reg" section. + Original upage address X is at location core_reg_sect+x+reg_addr. + */ + +static void +fetch_core_registers (core_reg_sect, core_reg_size, which, reg_addr) + char *core_reg_sect; + unsigned core_reg_size; + int which; + CORE_ADDR reg_addr; +{ + register int regno; + register int cregno; + register int addr; + int bad_reg = -1; + int offset; + struct user *tmp_uaddr; + + /* + * First get virtual address of user structure. Then calculate offset. + */ + memcpy(&tmp_uaddr, + &((struct user *) core_reg_sect)->u_kproc.ki_addr, + sizeof(tmp_uaddr)); + offset = -reg_addr - (int) tmp_uaddr; + + for (regno = 0; regno < NUM_REGS; regno++) + { + cregno = tregmap[regno]; + if (cregno == tGS) + addr = offsetof (struct user, u_pcb) + offsetof (struct pcb, pcb_gs); + else + addr = offset + 4 * cregno; + if (addr < 0 || addr >= core_reg_size) + { + if (bad_reg < 0) + bad_reg = regno; + } + else + { + supply_register (regno, core_reg_sect + addr); + } + } + if (bad_reg >= 0) + { + error ("Register %s not found in core file.", gdb_register_names[bad_reg]); + } + + addr = offsetof (struct user, u_pcb) + offsetof (struct pcb, pcb_savefpu); + memcpy (&pcb_savefpu, core_reg_sect + addr, sizeof pcb_savefpu); +} + +#ifdef FLOAT_INFO +#include "expression.h" +#include "language.h" /* for local_hex_string */ +#include "floatformat.h" + +#include <sys/param.h> +#include <signal.h> +#include <sys/ioctl.h> +#include <fcntl.h> + +#include <a.out.h> + +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/uio.h> +#define curpcb Xcurpcb /* XXX avoid leaking declaration from pcb.h */ +#include <sys/user.h> +#undef curpcb +#include <sys/file.h> +#include "gdb_stat.h" +#include <sys/ptrace.h> + +extern void print_387_control_word (); /* i387-tdep.h */ +extern void print_387_status_word (); + +#define fpstate save87 +#define U_FPSTATE(u) u.u_pcb.pcb_savefpu + +static void +i387_to_double (from, to) + char *from; + char *to; +{ + long *lp; + /* push extended mode on 387 stack, then pop in double mode + * + * first, set exception masks so no error is generated - + * number will be rounded to inf or 0, if necessary + */ + asm ("pushl %eax"); /* grab a stack slot */ + asm ("fstcw (%esp)"); /* get 387 control word */ + asm ("movl (%esp),%eax"); /* save old value */ + asm ("orl $0x3f,%eax"); /* mask all exceptions */ + asm ("pushl %eax"); + asm ("fldcw (%esp)"); /* load new value into 387 */ + + asm ("movl 8(%ebp),%eax"); + asm ("fldt (%eax)"); /* push extended number on 387 stack */ + asm ("fwait"); + asm ("movl 12(%ebp),%eax"); + asm ("fstpl (%eax)"); /* pop double */ + asm ("fwait"); + + asm ("popl %eax"); /* flush modified control word */ + asm ("fnclex"); /* clear exceptions */ + asm ("fldcw (%esp)"); /* restore original control word */ + asm ("popl %eax"); /* flush saved copy */ +} + +struct env387 +{ + unsigned short control; + unsigned short r0; + unsigned short status; + unsigned short r1; + unsigned short tag; + unsigned short r2; + unsigned long eip; + unsigned short code_seg; + unsigned short opcode; + unsigned long operand; + unsigned short operand_seg; + unsigned short r3; + unsigned char regs[8][10]; +}; + +static void +print_387_status (status, ep) + unsigned short status; + struct env387 *ep; +{ + int i; + int bothstatus; + int top; + int fpreg; + + bothstatus = ((status != 0) && (ep->status != 0)); + if (status != 0) + { + if (bothstatus) + printf_unfiltered ("u: "); + print_387_status_word ((unsigned int)status); + } + + if (ep->status != 0) + { + if (bothstatus) + printf_unfiltered ("e: "); + print_387_status_word ((unsigned int)ep->status); + } + + print_387_control_word ((unsigned int)ep->control); + printf_unfiltered ("last instruction: "); + printf_unfiltered ("opcode %s; ", local_hex_string(ep->opcode)); + printf_unfiltered ("pc %s:", local_hex_string(ep->code_seg)); + printf_unfiltered ("%s; ", local_hex_string(ep->eip)); + printf_unfiltered ("operand %s", local_hex_string(ep->operand_seg)); + printf_unfiltered (":%s\n", local_hex_string(ep->operand)); + + top = (ep->status >> 11) & 7; + + printf_unfiltered (" regno tag msb lsb value\n"); + for (fpreg = 7; fpreg >= 0; fpreg--) + { + int exp; + int mantissa_or; + int normal; + char *sign; + int st_regno; + unsigned short *usregs; + double val; + + /* The physical regno `fpreg' is only relevant as an index into the + * tag word. Logical `%st' numbers are required for indexing ep->regs. + */ + st_regno = (fpreg + 8 - top) & 7; + + printf_unfiltered ("%%st(%d) %s ", st_regno, fpreg == top ? "=>" : " "); + + switch ((ep->tag >> (fpreg * 2)) & 3) + { + case 0: printf_unfiltered ("valid "); break; + case 1: printf_unfiltered ("zero "); break; + case 2: printf_unfiltered ("trap "); break; + case 3: printf_unfiltered ("empty "); break; + } + for (i = 9; i >= 0; i--) + printf_unfiltered ("%02x", ep->regs[st_regno][i]); + + printf_unfiltered (" "); + + /* + * Handle weird cases better than floatformat_to_double () and + * printf (). + */ + usregs = (unsigned short *) ep->regs[st_regno]; + sign = usregs[4] & 0x8000 ? "-" : ""; + exp = usregs[4] & 0x7fff; + normal = usregs[3] & 0x8000; + mantissa_or = usregs[0] | usregs[1] | usregs[2] | (usregs[3] & 0x7fff); + if (exp == 0) + { + if (normal) + printf_unfiltered ("Pseudo Denormal (0 as a double)"); + else if (mantissa_or == 0) + printf_unfiltered ("%s0", sign); + else + printf_unfiltered ("Denormal (0 as a double)"); + } + else if (exp == 0x7fff) + { + if (!normal) + printf_unfiltered ("Pseudo "); + if (mantissa_or == 0) + printf_unfiltered ("%sInf", sign); + else + printf_unfiltered ("%s NaN", + usregs[3] & 0x4000 ? "Quiet" : "Signaling"); + if (!normal) + printf_unfiltered (" (NaN)"); + } + else if (!normal) + printf_unfiltered ("Unnormal (NaN)"); + else + { +#if 0 + /* Use this we stop trapping on overflow. */ + floatformat_to_double(&floatformat_i387_ext, + (char *) ep->regs[st_regno], &val); +#else + i387_to_double((char *) ep->regs[st_regno], (char *) &val); +#endif + printf_unfiltered ("%g", val); + } + printf_unfiltered ("\n"); + } +} + +void +i386_float_info () +{ + struct user u; /* just for address computations */ + int i; + /* fpstate defined in <sys/user.h> */ + struct fpstate *fpstatep; + char buf[sizeof (struct fpstate) + 2 * sizeof (int)]; + unsigned int uaddr; + char fpvalid; + unsigned int rounded_addr; + unsigned int rounded_size; + /*extern int corechan;*/ + int skip; + extern int inferior_pid; + + uaddr = (char *)&U_FPSTATE(u) - (char *)&u; + if (inferior_pid != 0 && core_bfd == NULL) + { + int pid = inferior_pid & ((1 << 17) - 1); /* XXX extract pid from tid */ + ptrace(PT_GETFPREGS, pid, &buf[0], sizeof(struct fpreg)); + fpstatep = (struct fpstate *)&buf[0]; + } + else + fpstatep = &pcb_savefpu; + + print_387_status (fpstatep->sv_ex_sw, (struct env387 *)fpstatep); +} +#endif /* FLOAT_INFO */ + +int +kernel_u_size () +{ + return (sizeof (struct user)); +} + +#ifdef SETUP_ARBITRARY_FRAME +#include "frame.h" +struct frame_info * +setup_arbitrary_frame (argc, argv) + int argc; + CORE_ADDR *argv; +{ + if (argc != 2) + error ("i386 frame specifications require two arguments: sp and pc"); + + return create_new_frame (argv[0], argv[1]); +} +#endif /* SETUP_ARBITRARY_FRAME */ + +#ifdef HAVE_GREGSET_T +void +supply_gregset (gp) + gregset_t *gp; +{ + int regno = 0; + + /* These must be ordered the same as REGISTER_NAMES in + config/i386/tm-i386.h. */ + supply_register (regno++, (char *)&gp->r_eax); + supply_register (regno++, (char *)&gp->r_ecx); + supply_register (regno++, (char *)&gp->r_edx); + supply_register (regno++, (char *)&gp->r_ebx); + supply_register (regno++, (char *)&gp->r_esp); + supply_register (regno++, (char *)&gp->r_ebp); + supply_register (regno++, (char *)&gp->r_esi); + supply_register (regno++, (char *)&gp->r_edi); + supply_register (regno++, (char *)&gp->r_eip); + supply_register (regno++, (char *)&gp->r_eflags); + supply_register (regno++, (char *)&gp->r_cs); + supply_register (regno++, (char *)&gp->r_ss); + supply_register (regno++, (char *)&gp->r_ds); + supply_register (regno++, (char *)&gp->r_es); + supply_register (regno++, (char *)&gp->r_fs); + supply_register (regno++, (char *)&gp->r_gs); +} +#endif /* HAVE_GREGSET_T */ + +#ifdef HAVE_FPREGSET_T +void +supply_fpregset (fp) + fpregset_t *fp; +{ + memcpy (&pcb_savefpu, fp, sizeof pcb_savefpu); +} +#endif /* HAVE_FPREGSET_T */ + +/* Register that we are able to handle aout (trad-core) file formats. */ + +static struct core_fns aout_core_fns = +{ + bfd_target_unknown_flavour, + fetch_core_registers, + NULL +}; + +void +_initialize_core_aout () +{ + add_core_fns (&aout_core_fns); +} + +#ifdef PT_GETDBREGS + +/* + * 0: no trace output + * 1: trace watchpoint requests + * 2: trace `watchpoint hit?' tests, too + */ +#define WATCHPOINT_DEBUG 0 + +#include "breakpoint.h" + +int +can_watch(type, cnt, ot) + int type, cnt, ot; +{ + int rv; + static int cnt_watch, cnt_awatch; + + switch (type) + { + case bp_hardware_watchpoint: + cnt_watch = cnt; + break; + + case bp_access_watchpoint: + cnt_awatch = cnt; + break; + + default: + rv = 0; + goto overandout; + } + + rv = cnt_watch + cnt_awatch <= 4? 1: -1; + + overandout: +#if WATCHPOINT_DEBUG + printf_filtered("can_watch(%d, %d, %d) = %d (counts: w: %d, rw: %d)\n", + type, cnt, ot, rv, cnt_watch, cnt_awatch); +#endif + + return rv; +} + +int +stopped_by_watchpoint() +{ + struct dbreg dbr; + extern int inferior_pid; + + if (current_target.to_shortname == 0 || + ! (strcmp(current_target.to_shortname, "child") == 0 || + strcmp(current_target.to_shortname, "freebsd-uthreads") == 0)) + return 0; + + if (inferior_pid != 0 && core_bfd == NULL) + { + int pid = inferior_pid & ((1 << 17) - 1); /* XXX extract pid from tid */ + + if (ptrace(PT_GETDBREGS, pid, (caddr_t)&dbr, 0) == -1) + { + perror("ptrace(PT_GETDBREGS) failed"); + return 0; + } +#if WATCHPOINT_DEBUG > 1 + printf_filtered("stopped_by_watchpoint(): DR6 = %#x\n", dbr.dr6); +#endif + /* + * If a hardware watchpoint was hit, one of the lower 4 bits in + * DR6 is set (the actual bit indicates which of DR0...DR3 triggered + * the trap). + */ + return dbr.dr6 & 0x0f; + } + else + { + warning("Can't set a watchpoint on a core file."); + return 0; + } +} + +int +insert_watchpoint(addr, len, type) + int addr, len, type; +{ + struct dbreg dbr; + extern int inferior_pid; + + if (current_target.to_shortname == 0 || + ! (strcmp(current_target.to_shortname, "child") == 0 || + strcmp(current_target.to_shortname, "freebsd-uthreads") == 0)) + return 0; + + if (inferior_pid != 0 && core_bfd == NULL) + { + int pid = inferior_pid & ((1 << 17) - 1); /* XXX extract pid from tid */ + int i, mask; + unsigned int sbits; + + if (ptrace(PT_GETDBREGS, pid, (caddr_t)&dbr, 0) == -1) + { + perror("ptrace(PT_GETDBREGS) failed"); + return 0; + } + + for (i = 0, mask = 0x03; i < 4; i++, mask <<= 2) + if ((dbr.dr7 & mask) == 0) + break; + if (i >= 4) { + warning("no more hardware watchpoints available"); + return -1; + } + + /* paranoia */ + if (len > 4) + { + warning("watchpoint length %d unsupported, using lenght = 4", + len); + len = 4; + } + else if (len == 3) + { + warning("weird watchpoint length 3, using 2"); + len = 2; + } + else if (len == 0) + { + warning("weird watchpoint length 0, using 1"); + len = 1; + } + + switch (len) + { + case 1: sbits = 0; break; + case 2: sbits = 4; break; + case 4: sbits = 0x0c; break; + } + + /* + * The `type' value is 0 for `watch on write', 1 for `watch on + * read', 2 for `watch on both'. The i386 debug register + * breakpoint types are 0 for `execute' (not used in GDB), 1 for + * `write', and 4 for `read/write'. Plain `read' trapping is + * not supported on i386, value 3 is illegal. + */ + switch (type) + { + default: + warning("weird watchpoint type %d, using a write watchpoint"); + /* FALLTHROUGH */ + case 0: + sbits |= 1; + break; + + case 2: + sbits |= 3; + break; + } + sbits <<= 4 * i + 16; + sbits |= 1 << 2 * i; + + dbr.dr7 |= sbits; + *(&dbr.dr0 + i) = (unsigned int)addr; + +#if WATCHPOINT_DEBUG + printf_filtered("insert_watchpoint(), inserting DR7 = %#x, DR%d = %#x\n", + dbr.dr7, i, addr); +#endif + if (ptrace(PT_SETDBREGS, pid, (caddr_t)&dbr, 0) == -1) + { + perror("ptrace(PT_SETDBREGS) failed"); + return 0; + } + } + else + { + warning("Can't set a watchpoint on a core file."); + return 0; + } +} + +int +remove_watchpoint(addr, len, type) + int addr, len, type; +{ + struct dbreg dbr; + extern int inferior_pid; + + if (current_target.to_shortname == 0 || + ! (strcmp(current_target.to_shortname, "child") == 0 || + strcmp(current_target.to_shortname, "freebsd-uthreads") == 0)) + return 0; + + if (inferior_pid != 0 && core_bfd == NULL) + { + int pid = inferior_pid & ((1 << 17) - 1); /* XXX extract pid from tid */ + int i; + unsigned int sbits, *dbregp; + + if (ptrace(PT_GETDBREGS, pid, (caddr_t)&dbr, 0) == -1) + { + perror("ptrace(PT_GETDBREGS) failed"); + return 0; + } + + for (i = 0, dbregp = &dbr.dr0; i < 4; i++, dbregp++) + if (*dbregp == (unsigned int)addr) + break; + if (i >= 4) + { + warning("watchpoint for address %#x not found", addr); + return -1; + } + + *dbregp = 0; + sbits = 0xf << (4 * i + 16); + sbits |= 3 << 2 * i; + dbr.dr7 &= ~sbits; + +#if WATCHPOINT_DEBUG + printf_filtered("remove_watchpoint(): removing watchpoint for %#x, DR7 = %#x\n", + addr, dbr.dr7); +#endif + if (ptrace(PT_SETDBREGS, pid, (caddr_t)&dbr, 0) == -1) + { + perror("ptrace(PT_SETDBREGS) failed"); + return 0; + } + } + else + { + warning("Can't set a watchpoint on a core file."); + return 0; + } +} + +#endif /* PT_GETDBREGS */ diff --git a/gnu/usr.bin/binutils/gdb5/i386/kvm-fbsd.c b/gnu/usr.bin/binutils/gdb5/i386/kvm-fbsd.c new file mode 100644 index 0000000..9f2d69d --- /dev/null +++ b/gnu/usr.bin/binutils/gdb5/i386/kvm-fbsd.c @@ -0,0 +1,995 @@ +/* Live and postmortem kernel debugging functions for FreeBSD. + Copyright 1996 Free Software Foundation, Inc. + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 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. */ + +/* $FreeBSD$ */ + +#include "defs.h" + +#include <errno.h> +#include <signal.h> +#include <fcntl.h> +#include <paths.h> +#include <sys/sysctl.h> +#include <sys/param.h> +#include <sys/time.h> +#include <sys/proc.h> +#include <sys/user.h> +#include "frame.h" /* required by inferior.h */ +#include "inferior.h" +#include "symtab.h" +#include "symfile.h" +#include "objfiles.h" +#include "command.h" +#include "bfd.h" +#include "target.h" +#include "gdbcore.h" +#include <sys/stat.h> +#include <unistd.h> +#include <vm/vm.h> +#include <vm/vm_param.h> + +#include <machine/vmparam.h> +#include <machine/pcb.h> +#include <machine/tss.h> +#include <machine/frame.h> +#include <machine/globaldata.h> + +static void kcore_files_info PARAMS ((struct target_ops *)); + +static void kcore_close PARAMS ((int)); + +static void get_kcore_registers PARAMS ((int)); + +static int kcore_xfer_kmem PARAMS ((CORE_ADDR, char *, int, int, struct target_ops *)); + +static int xfer_umem PARAMS ((CORE_ADDR, char *, int, int)); + +static CORE_ADDR ksym_lookup PARAMS ((const char *)); + +static int read_pcb PARAMS ((int, CORE_ADDR)); + +static struct proc * curProc PARAMS ((void)); + +static int set_proc_context PARAMS ((CORE_ADDR paddr)); + +static void kcore_open PARAMS ((char *filename, int from_tty)); + +static void kcore_detach PARAMS ((char *args, int from_tty)); + +static void set_proc_cmd PARAMS ((char *arg, int from_tty)); + +static void set_cpu_cmd PARAMS ((char *arg, int from_tty)); + +static CORE_ADDR kvtophys PARAMS ((int, CORE_ADDR)); + +static int physrd PARAMS ((int, u_int, char*, int)); + +static int kvm_open PARAMS ((const char *efile, char *cfile, char *sfile, + int perm, char *errout)); + +static int kvm_close PARAMS ((int fd)); + +static int kvm_write PARAMS ((int core_kd, CORE_ADDR memaddr, + char *myaddr, int len)); + +static int kvm_read PARAMS ((int core_kd, CORE_ADDR memaddr, + char *myaddr, int len)); + +static int kvm_uread PARAMS ((int core_kd, struct proc *p, + CORE_ADDR memaddr, char *myaddr, + int len)); + +static int kernel_core_file_hook PARAMS ((int fd, CORE_ADDR addr, + char *buf, int len)); + +static CORE_ADDR kvm_getpcpu PARAMS ((int cfd, int cpuid)); + +static struct kinfo_proc * kvm_getprocs PARAMS ((int cfd, int op, + CORE_ADDR proc, int *cnt)); + +extern struct target_ops kcore_ops; /* Forward decl */ + +/* Non-zero means we are debugging a kernel core file */ +int kernel_debugging = 0; +int kernel_writablecore = 0; + +static char *core_file; +static int core_kd = -1; +static struct proc *cur_proc; +static CORE_ADDR kernel_start; + +static CORE_ADDR pcpu; +#define PCPU_OFFSET(name) \ + offsetof(struct globaldata, gd_ ## name) + +/* + * Symbol names of kernel entry points. Use special frames. + */ +#define KSYM_TRAP "calltrap" +#define KSYM_INTERRUPT "Xresume" +#define KSYM_SYSCALL "Xsyscall" + +/* + * Read the "thing" at kernel address 'addr' into the space pointed to + * by point. The length of the "thing" is determined by the type of p. + * Result is non-zero if transfer fails. + */ +#define kvread(addr, p) \ + (target_read_memory ((CORE_ADDR)(addr), (char *)(p), sizeof(*(p)))) + + + +/* + * The following is FreeBSD-specific hackery to decode special frames + * and elide the assembly-language stub. This could be made faster by + * defining a frame_type field in the machine-dependent frame information, + * but we don't think that's too important right now. + */ +enum frametype { tf_normal, tf_trap, tf_interrupt, tf_syscall }; + +CORE_ADDR +fbsd_kern_frame_saved_pc (fr) +struct frame_info *fr; +{ + struct minimal_symbol *sym; + CORE_ADDR this_saved_pc; + enum frametype frametype; + + this_saved_pc = read_memory_integer (fr->frame + 4, 4); + sym = lookup_minimal_symbol_by_pc (this_saved_pc); + frametype = tf_normal; + if (sym != NULL) { + if (strcmp (SYMBOL_NAME(sym), KSYM_TRAP) == 0) + frametype = tf_trap; + else if (strncmp (SYMBOL_NAME(sym), KSYM_INTERRUPT, 7) == 0) + frametype = tf_interrupt; + else if (strcmp (SYMBOL_NAME(sym), KSYM_SYSCALL) == 0) + frametype = tf_syscall; + } + + switch (frametype) { + case tf_normal: + return (this_saved_pc); + +#define oEIP offsetof(struct trapframe, tf_eip) + + case tf_trap: + return (read_memory_integer (fr->frame + 8 + oEIP, 4)); + + case tf_interrupt: + return (read_memory_integer (fr->frame + 16 + oEIP, 4)); + + case tf_syscall: + return (read_memory_integer (fr->frame + 8 + oEIP, 4)); +#undef oEIP + } +} + +static CORE_ADDR +ksym_lookup (name) +const char *name; +{ + struct minimal_symbol *sym; + + sym = lookup_minimal_symbol (name, NULL, NULL); + if (sym == NULL) + error ("kernel symbol `%s' not found.", name); + + return SYMBOL_VALUE_ADDRESS (sym); +} + +static struct proc * +curProc () +{ + struct proc *p; + CORE_ADDR addr = pcpu + PCPU_OFFSET (curproc); + + if (kvread (addr, &p)) + error ("cannot read proc pointer at %x\n", addr); + return p; +} + +/* + * Set the process context to that of the proc structure at + * system address paddr. + */ +static int +set_proc_context (paddr) + CORE_ADDR paddr; +{ + struct proc p; + + if (paddr < kernel_start) + return (1); + + cur_proc = (struct proc *)paddr; +#ifdef notyet + set_kernel_boundaries (cur_proc); +#endif + + /* Fetch all registers from core file */ + target_fetch_registers (-1); + + /* Now, set up the frame cache, and print the top of stack */ + flush_cached_frames (); + set_current_frame (create_new_frame (read_fp (), read_pc ())); + select_frame (get_current_frame (), 0); + return (0); +} + +/* Discard all vestiges of any previous core file + and mark data and stack spaces as empty. */ + +/* ARGSUSED */ +static void +kcore_close (quitting) + int quitting; +{ + inferior_pid = 0; /* Avoid confusion from thread stuff */ + + if (core_kd) + { + kvm_close (core_kd); + free (core_file); + core_file = NULL; + core_kd = -1; + } +} + +/* This routine opens and sets up the core file bfd */ + +static void +kcore_open (filename, from_tty) + char *filename; + int from_tty; +{ + const char *p; + struct cleanup *old_chain; + char buf[256], *cp; + int ontop; + CORE_ADDR addr; + struct pcb pcb; + + target_preopen (from_tty); + + unpush_target (&kcore_ops); + + if (!filename) + { + /*error (core_kd?*/ + error ( (core_kd >= 0)? + "No core file specified. (Use `detach' to stop debugging a core file.)" + : "No core file specified."); + } + + filename = tilde_expand (filename); + if (filename[0] != '/') + { + cp = concat (current_directory, "/", filename, NULL); + free (filename); + filename = cp; + } + + old_chain = make_cleanup (free, filename); + + /* + * gdb doesn't really do anything if the exec-file couldn't + * be opened (in that case exec_bfd is NULL). Usually that's + * no big deal, but kvm_open needs the exec-file's name, + * which results in dereferencing a NULL pointer, a real NO-NO ! + * So, check here if the open of the exec-file succeeded. + */ + if (exec_bfd == NULL) /* the open failed */ + error ("kgdb could not open the exec-file, please check the name you used !"); + + core_kd = kvm_open (exec_bfd->filename, filename, NULL, + kernel_writablecore? O_RDWR : O_RDONLY, "kgdb: "); + if (core_kd < 0) + perror_with_name (filename); + + /* Looks semi-reasonable. Toss the old core file and work on the new. */ + + discard_cleanups (old_chain); /* Don't free filename any more */ + core_file = filename; + ontop = !push_target (&kcore_ops); + + kernel_start = bfd_get_start_address (exec_bfd); /* XXX */ + + /* print out the panic string if there is one */ + if (kvread (ksym_lookup ("panicstr"), &addr) == 0 + && addr != 0 + && target_read_memory (addr, buf, sizeof (buf)) == 0) + { + for (cp = buf; cp < &buf[sizeof (buf)] && *cp; cp++) + if (!isascii (*cp) || (!isprint (*cp) && !isspace (*cp))) + *cp = '?'; + *cp = '\0'; + if (buf[0] != '\0') + printf ("panicstr: %s\n", buf); + } + + /* Print all the panic messages if possible. */ + if (symfile_objfile != NULL) + { + printf ("panic messages:\n---\n"); + snprintf (buf, sizeof buf, + "/sbin/dmesg -N %s -M %s | \ + /usr/bin/awk '/^(panic:|Fatal trap) / { printing = 1 } \ + { if (printing) print $0 }'", + symfile_objfile->name, filename); + fflush(stdout); + system (buf); + printf ("---\n"); + } + + if (!ontop) + { + warning ("you won't be able to access this core file until you terminate\n\ +your %s; do ``info files''", target_longname); + return; + } + + /* we may need this later */ + cur_proc = (struct proc *)curProc (); + /* Now, set up the frame cache, and print the top of stack */ + flush_cached_frames (); + set_current_frame (create_new_frame (read_fp (), read_pc ())); + select_frame (get_current_frame (), 0); + print_stack_frame (selected_frame, selected_frame_level, 1); +} + +static void +kcore_detach (args, from_tty) + char *args; + int from_tty; +{ + if (args) + error ("Too many arguments"); + unpush_target (&kcore_ops); + reinit_frame_cache (); + if (from_tty) + printf_filtered ("No kernel core file now.\n"); +} + +/* Get the registers out of a core file. This is the machine- + independent part. Fetch_core_registers is the machine-dependent + part, typically implemented in the xm-file for each architecture. */ + +/* We just get all the registers, so we don't use regno. */ +/* ARGSUSED */ +static void +get_kcore_registers (regno) + int regno; +{ + struct user *uaddr; + + /* find the pcb for the current process */ + if (cur_proc == NULL || kvread (&cur_proc->p_addr, &uaddr)) + error ("cannot read u area ptr for proc at %#x", cur_proc); + if (read_pcb (core_kd, (CORE_ADDR)&uaddr->u_pcb) < 0) + error ("cannot read pcb at %#x", &uaddr->u_pcb); +} + +static void +kcore_files_info (t) + struct target_ops *t; +{ + printf ("\t`%s'\n", core_file); +} + +static int +kcore_xfer_kmem (memaddr, myaddr, len, write, target) + CORE_ADDR memaddr; + char *myaddr; + int len; + int write; + struct target_ops *target; +{ + int ns; + int nu; + + if (memaddr >= (CORE_ADDR)VM_MAXUSER_ADDRESS) + nu = 0; + else + { + nu = xfer_umem (memaddr, myaddr, len, write); + if (nu <= 0) + return (0); + if (nu == len) + return (nu); + memaddr += nu; + if (memaddr != (CORE_ADDR)VM_MAXUSER_ADDRESS) + return (nu); + myaddr += nu; + len -= nu; + } + + ns = (write ? kvm_write : kvm_read) (core_kd, memaddr, myaddr, len); + if (ns < 0) + ns = 0; + + return (nu + ns); +} + +static int +xfer_umem (memaddr, myaddr, len, write) + CORE_ADDR memaddr; + char *myaddr; + int len; + int write; /* ignored */ +{ + int n; + struct proc proc; + + if (cur_proc == NULL || kvread (cur_proc, &proc)) + error ("cannot read proc at %#x", cur_proc); + n = kvm_uread (core_kd, &proc, memaddr, myaddr, len) ; + + if (n < 0) + return 0; + return n; +} + +#define KERNOFF ((unsigned)KERNBASE) +#define INKERNEL(x) ((x) >= KERNOFF) + +static CORE_ADDR sbr; +static CORE_ADDR curpcb; +static int found_pcb; +static int devmem; +static int kfd; +static struct pcb pcb; + +static void +set_proc_cmd (arg, from_tty) + char *arg; + int from_tty; +{ + CORE_ADDR paddr; + struct kinfo_proc *kp; + int cnt = 0; + + if (!arg) + error_no_arg ("proc address for new current process"); + if (!kernel_debugging) + error ("not debugging kernel"); + + paddr = (CORE_ADDR)parse_and_eval_address (arg); + /* assume it's a proc pointer if it's in the kernel */ + if (paddr >= kernel_start) { + if (set_proc_context(paddr)) + error("invalid proc address"); + } else { + kp = kvm_getprocs(core_kd, KERN_PROC_PID, paddr, &cnt); + if (!cnt) + error("invalid pid"); + if (set_proc_context((CORE_ADDR)kp->ki_paddr)) + error("invalid proc address"); + } +} + +static void +set_cpu_cmd (arg, from_tty) + char *arg; + int from_tty; +{ + CORE_ADDR paddr; + CORE_ADDR pcaddr; + struct kinfo_proc *kp; + int cpu, cfd; + + if (!arg) + error_no_arg ("cpu number"); + if (!kernel_debugging) + error ("not debugging kernel"); + + cfd = core_kd; + cpu = (int)parse_and_eval_address (arg); + if ((pcaddr = kvm_getpcpu (cfd, cpu)) == NULL) + error ("cpu number out of range"); + + pcpu = pcaddr; + curpcb = kvtophys(cfd, pcpu + PCPU_OFFSET (curpcb)); + physrd (cfd, curpcb, (char*)&curpcb, sizeof curpcb); + + if (!devmem) + paddr = ksym_lookup ("dumppcb") - KERNOFF; + else + paddr = kvtophys (cfd, curpcb); + read_pcb (cfd, paddr); + printf ("initial pcb at %lx\n", (unsigned long)paddr); + + if ((cur_proc = curProc())) + target_fetch_registers (-1); + + /* Now, set up the frame cache, and print the top of stack */ + flush_cached_frames (); + set_current_frame (create_new_frame (read_fp (), read_pc ())); + select_frame (get_current_frame (), 0); + print_stack_frame (selected_frame, selected_frame_level, 1); +} + +/* substitutes for the stuff in libkvm which doesn't work */ +/* most of this was taken from the old kgdb */ + +/* we don't need all this stuff, but the call should look the same */ + +static int +kvm_open (efile, cfile, sfile, perm, errout) + const char *efile; + char *cfile; + char *sfile; /* makes this kvm_open more compatible to the one in libkvm */ + int perm; + char *errout; /* makes this kvm_open more compatible to the one in libkvm */ +{ + struct stat stb; + int cfd; + CORE_ADDR paddr; + + if ((cfd = open (cfile, perm, 0)) < 0) + return (cfd); + + if ((pcpu = kvm_getpcpu (cfd, 0)) == NULL) + return (-1); + + fstat (cfd, &stb); + if ((stb.st_mode & S_IFMT) == S_IFCHR + && stb.st_rdev == makedev (2, 0)) + { + devmem = 1; + kfd = open (_PATH_KMEM, perm, 0); + } + + physrd (cfd, ksym_lookup ("IdlePTD") - KERNOFF, (char*)&sbr, sizeof sbr); + printf ("IdlePTD %lu\n", (unsigned long)sbr); + curpcb = kvtophys(cfd, pcpu + PCPU_OFFSET (curpcb)); + physrd (cfd, curpcb, (char*)&curpcb, sizeof curpcb); + + found_pcb = 1; /* for vtophys */ + if (!devmem) + paddr = ksym_lookup ("dumppcb") - KERNOFF; + else + paddr = kvtophys (cfd, curpcb); + read_pcb (cfd, paddr); + printf ("initial pcb at %lx\n", (unsigned long)paddr); + + return (cfd); +} + +static int +kvm_close (fd) + int fd; +{ + return (close (fd)); +} + +static int +kvm_write (core_kd, memaddr, myaddr, len) + int core_kd; + CORE_ADDR memaddr; + char *myaddr; +{ + int cc; + + if (devmem) + { + if (kfd > 0) + { + /* + * Just like kvm_read, only we write. + */ + errno = 0; + if (lseek (kfd, (off_t)memaddr, 0) < 0 + && errno != 0) + { + error ("kvm_write:invalid address (%x)", memaddr); + return (0); + } + cc = write (kfd, myaddr, len); + if (cc < 0) + { + error ("kvm_write:write failed"); + return (0); + } + else if (cc < len) + error ("kvm_write:short write"); + return (cc); + } + else + return (0); + } + else + { + printf ("kvm_write not implemented for dead kernels\n"); + return (0); + } + /* NOTREACHED */ +} + +static int +kvm_read (core_kd, memaddr, myaddr, len) + int core_kd; + CORE_ADDR memaddr; + char *myaddr; +{ + return (kernel_core_file_hook (core_kd, memaddr, myaddr, len)); +} + +static int +kvm_uread (core_kd, p, memaddr, myaddr, len) + int core_kd; + register struct proc *p; + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + register char *cp; + char procfile[MAXPATHLEN]; + ssize_t amount; + int fd; + + if (devmem) + { + sprintf (procfile, "/proc/%d/mem", p->p_pid); + fd = open (procfile, O_RDONLY, 0); + if (fd < 0) + { + error ("cannot open %s", procfile); + close (fd); + return (0); + } + + cp = myaddr; + while (len > 0) + { + errno = 0; + if (lseek (fd, (off_t)memaddr, 0) == -1 && errno != 0) + { + error ("invalid address (%x) in %s", memaddr, procfile); + break; + } + amount = read (fd, cp, len); + if (amount < 0) + { + error ("error reading %s", procfile); + break; + } + if (amount == 0) + { + error ("EOF reading %s", procfile); + break; + } + cp += amount; + memaddr += amount; + len -= amount; + } + + close (fd); + return ((ssize_t) (cp - myaddr)); + } + else + return (kernel_core_file_hook (core_kd, memaddr, myaddr, len)); +} + +static struct kinfo_proc kp; + +/* + * try to do what kvm_proclist in libkvm would do + */ +static int +kvm_proclist (cfd, pid, p, cnt) +int cfd, pid, *cnt; +struct proc *p; +{ + struct proc lp; + + for (; p != NULL; p = LIST_NEXT(&lp, p_list)) { + if (!kvm_read(cfd, (CORE_ADDR)p, (char *)&lp, sizeof (lp))) + return (0); + if (lp.p_pid != pid) + continue; + kp.ki_paddr = p; + *cnt = 1; + return (1); + } + *cnt = 0; + return (0); +} + +/* + * try to do what kvm_deadprocs in libkvm would do + */ +static struct kinfo_proc * +kvm_deadprocs (cfd, pid, cnt) +int cfd, pid, *cnt; +{ + CORE_ADDR allproc, zombproc; + struct proc *p; + + allproc = ksym_lookup("allproc"); + if (kvm_read(cfd, allproc, (char *)&p, sizeof (p)) == 0) + return (NULL); + kvm_proclist (cfd, pid, p, cnt); + if (!*cnt) { + zombproc = ksym_lookup("zombproc"); + if (kvm_read(cfd, zombproc, (char *)&p, sizeof (p)) == 0) + return (NULL); + kvm_proclist (cfd, pid, p, cnt); + } + return (&kp); +} + +static CORE_ADDR +kvm_getpcpu (cfd, cpuid) +int cfd, cpuid; +{ + SLIST_HEAD(, globaldata) pcpu_head; + struct globaldata lgd; + struct globaldata *gd; + + physrd (cfd, ksym_lookup ("cpuhead") - KERNOFF, (char*)&pcpu_head, + sizeof pcpu_head); + gd = SLIST_FIRST (&pcpu_head); + for (; gd != NULL; gd = SLIST_NEXT (&lgd, gd_allcpu)) + { + kvm_read (cfd, (CORE_ADDR)gd, (char*)&lgd, sizeof lgd); + if (lgd.gd_cpuid == cpuid) + break; + } + + return ((CORE_ADDR)gd); +} + +/* + * try to do what kvm_getprocs in libkvm would do + */ +static struct kinfo_proc * +kvm_getprocs (cfd, op, proc, cnt) +int cfd, op, *cnt; +CORE_ADDR proc; +{ + int mib[4], size; + + *cnt = 0; + /* assume it's a pid */ + if (devmem) { /* "live" kernel, use sysctl */ + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = (int)proc; + size = sizeof (kp); + if (sysctl (mib, 4, &kp, &size, NULL, 0) < 0) { + perror("sysctl"); + *cnt = 0; + return (NULL); + } + if (!size) + *cnt = 0; + else + *cnt = 1; + return (&kp); + } else + return (kvm_deadprocs (cfd, (int)proc, cnt)); +} + +static int +physrd (cfd, addr, dat, len) + int cfd; + u_int addr; + char *dat; + int len; +{ + if (lseek (cfd, (off_t)addr, L_SET) == -1) + return (-1); + return (read (cfd, dat, len)); +} + +static CORE_ADDR +kvtophys (fd, addr) + int fd; + CORE_ADDR addr; +{ + CORE_ADDR v; + unsigned int pte; + static CORE_ADDR PTD = -1; + CORE_ADDR current_ptd; + + /* + * We may no longer have a linear system page table... + * + * Here's the scoop. IdlePTD contains the physical address + * of a page table directory that always maps the kernel. + * IdlePTD is in memory that is mapped 1-to-1, so we can + * find it easily given its 'virtual' address from ksym_lookup(). + * For hysterical reasons, the value of IdlePTD is stored in sbr. + * + * To look up a kernel address, we first convert it to a 1st-level + * address and look it up in IdlePTD. This gives us the physical + * address of a page table page; we extract the 2nd-level part of + * VA and read the 2nd-level pte. Finally, we add the offset part + * of the VA into the physical address from the pte and return it. + * + * User addresses are a little more complicated. If we don't have + * a current PCB from read_pcb(), we use PTD, which is the (fixed) + * virtual address of the current ptd. Since it's NOT in 1-to-1 + * kernel space, we must look it up using IdlePTD. If we do have + * a pcb, we get the ptd from pcb_ptd. + */ + + if (INKERNEL (addr)) + current_ptd = sbr; + else if (found_pcb == 0) + { + if (PTD == -1) + PTD = kvtophys (fd, ksym_lookup ("PTD")); + current_ptd = PTD; + } + else + current_ptd = pcb.pcb_cr3; + + /* + * Read the first-level page table (ptd). + */ + v = current_ptd + ( (unsigned)addr >> PDRSHIFT) * sizeof pte; + if (physrd (fd, v, (char *)&pte, sizeof pte) < 0 || (pte&PG_V) == 0) + return (~0); + + if (pte & PG_PS) + { + /* + * No second-level page table; ptd describes one 4MB page. + * (We assume that the kernel wouldn't set PG_PS without enabling + * it cr0, and that the kernel doesn't support 36-bit physical + * addresses). + */ +#define PAGE4M_MASK (NBPDR - 1) +#define PG_FRAME4M (~PAGE4M_MASK) + addr = (pte & PG_FRAME4M) + (addr & PAGE4M_MASK); + } + else + { + /* + * Read the second-level page table. + */ + v = (pte&PG_FRAME) + ((addr >> PAGE_SHIFT)&(NPTEPG-1)) * sizeof pte; + if (physrd (fd, v, (char *) &pte, sizeof (pte)) < 0 || (pte&PG_V) == 0) + return (~0); + + addr = (pte & PG_FRAME) + (addr & PAGE_MASK); + } +#if 0 + printf ("vtophys (%x) -> %x\n", oldaddr, addr); +#endif + return (addr); +} + +static int +read_pcb (fd, uaddr) + int fd; + CORE_ADDR uaddr; +{ + int i; + int noreg; + CORE_ADDR nuaddr = uaddr; + + /* need this for the `proc' command to work */ + if (INKERNEL(uaddr)) + nuaddr = kvtophys(fd, uaddr); + + if (physrd (fd, nuaddr, (char *)&pcb, sizeof pcb) < 0) + { + error ("cannot read pcb at %x\n", uaddr); + return (-1); + } + + /* + * get the register values out of the sys pcb and + * store them where `read_register' will find them. + */ + /* + * XXX many registers aren't available. + * XXX for the non-core case, the registers are stale - they are for + * the last context switch to the debugger. + * XXX gcc's register numbers aren't all #defined in tm-i386.h. + */ + noreg = 0; + for (i = 0; i < 3; ++i) /* eax,ecx,edx */ + supply_register (i, (char *)&noreg); + supply_register (3, (char *)&pcb.pcb_ebx); + supply_register (SP_REGNUM, (char *)&pcb.pcb_esp); + supply_register (FP_REGNUM, (char *)&pcb.pcb_ebp); + supply_register (6, (char *)&pcb.pcb_esi); + supply_register (7, (char *)&pcb.pcb_edi); + supply_register (PC_REGNUM, (char *)&pcb.pcb_eip); + for (i = 9; i < 14; ++i) /* eflags, cs, ss, ds, es, fs */ + supply_register (i, (char *)&noreg); + supply_register (15, (char *)&pcb.pcb_gs); + + /* XXX 80387 registers? */ +} + +/* + * read len bytes from kernel virtual address 'addr' into local + * buffer 'buf'. Return numbert of bytes if read ok, 0 otherwise. On read + * errors, portion of buffer not read is zeroed. + */ + +static int +kernel_core_file_hook (fd, addr, buf, len) + int fd; + CORE_ADDR addr; + char *buf; + int len; +{ + int i; + CORE_ADDR paddr; + register char *cp; + int cc; + + cp = buf; + + while (len > 0) + { + paddr = kvtophys (fd, addr); + if (paddr == ~0) + { + memset (buf, '\000', len); + break; + } + /* we can't read across a page boundary */ + i = min (len, PAGE_SIZE - (addr & PAGE_MASK)); + if ( (cc = physrd (fd, paddr, cp, i)) <= 0) + { + memset (cp, '\000', len); + return (cp - buf); + } + cp += cc; + addr += cc; + len -= cc; + } + return (cp - buf); +} + +static struct target_ops kcore_ops; + +void +_initialize_kcorelow() +{ + kcore_ops.to_shortname = "kcore"; + kcore_ops.to_longname = "Kernel core dump file"; + kcore_ops.to_doc = + "Use a core file as a target. Specify the filename of the core file."; + kcore_ops.to_open = kcore_open; + kcore_ops.to_close = kcore_close; + kcore_ops.to_attach = find_default_attach; + kcore_ops.to_detach = kcore_detach; + kcore_ops.to_fetch_registers = get_kcore_registers; + kcore_ops.to_xfer_memory = kcore_xfer_kmem; + kcore_ops.to_files_info = kcore_files_info; + kcore_ops.to_create_inferior = find_default_create_inferior; + kcore_ops.to_stratum = kcore_stratum; + kcore_ops.to_has_memory = 1; + kcore_ops.to_has_stack = 1; + kcore_ops.to_has_registers = 1; + kcore_ops.to_magic = OPS_MAGIC; + + add_target (&kcore_ops); + add_com ("proc", class_obscure, set_proc_cmd, "Set current process context"); + add_com ("cpu", class_obscure, set_cpu_cmd, "Set current cpu"); +} diff --git a/gnu/usr.bin/binutils/gdb5/i386/nm.h b/gnu/usr.bin/binutils/gdb5/i386/nm.h new file mode 100644 index 0000000..1bf0c38 --- /dev/null +++ b/gnu/usr.bin/binutils/gdb5/i386/nm.h @@ -0,0 +1,170 @@ +/* $FreeBSD$ */ +/* Native-dependent definitions for Intel 386 running BSD Unix, for GDB. + Copyright 1986, 1987, 1989, 1992, 1996 Free Software Foundation, Inc. + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 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. */ + +/* $FreeBSD$ */ + +#ifndef NM_FREEBSD_H +#define NM_FREEBSD_H + +#define ATTACH_DETACH + +/* Be shared lib aware */ +#include "solib.h" +#ifdef FREEBSD_ELF +#define SVR4_SHARED_LIBS +#endif + +/* This is the amount to subtract from u.u_ar0 + to get the offset in the core file of the register values. */ + +#include <machine/vmparam.h> +#define KERNEL_U_ADDR USRSTACK + +#define REGISTER_U_ADDR(addr, blockend, regno) \ + (addr) = i386_register_u_addr ((blockend),(regno)); + +/* We define our own fetch/store methods */ +#define FETCH_INFERIOR_REGISTERS + +extern int +i386_register_u_addr PARAMS ((int, int)); + +#define PTRACE_ARG3_TYPE char* + +#ifndef FREEBSD_ELF + +/* make structure definitions match up with those expected in solib.c */ +#define link_object sod +#define lo_name sod_name +#define lo_library sod_library +#define lo_unused sod_reserved +#define lo_major sod_major +#define lo_minor sod_minor +#define lo_next sod_next + +#define link_map so_map +#define lm_addr som_addr +#define lm_name som_path +#define lm_next som_next +#define lm_lop som_sod +#define lm_lob som_sodbase +#define lm_rwt som_write +#define lm_ld som_dynamic +#define lm_lpd som_spd + +#define link_dynamic_2 section_dispatch_table +#define ld_loaded sdt_loaded +#define ld_need sdt_sods +#define ld_rules sdt_filler1 +#define ld_got sdt_got +#define ld_plt sdt_plt +#define ld_rel sdt_rel +#define ld_hash sdt_hash +#define ld_stab sdt_nzlist +#define ld_stab_hash sdt_filler2 +#define ld_buckets sdt_buckets +#define ld_symbols sdt_strings +#define ld_symb_size sdt_str_sz +#define ld_text sdt_text_sz +#define ld_plt_sz sdt_plt_sz + +#define rtc_symb rt_symbol +#define rtc_sp rt_sp +#define rtc_next rt_next + +#define ld_debug so_debug +#define ldd_version dd_version +#define ldd_in_debugger dd_in_debugger +#define ldd_sym_loaded dd_sym_loaded +#define ldd_bp_addr dd_bpt_addr +#define ldd_bp_inst dd_bpt_shadow +#define ldd_cp dd_cc + +#define link_dynamic _dynamic +#define ld_version d_version +#define ldd d_debug +#define ld_un d_un +#define ld_2 d_sdt + +#endif + +/* Return sizeof user struct to callers in less machine dependent routines */ + +#define KERNEL_U_SIZE kernel_u_size() +extern int kernel_u_size PARAMS ((void)); + +#define ADDITIONAL_OPTIONS \ + {"kernel", no_argument, &kernel_debugging, 1}, \ + {"k", no_argument, &kernel_debugging, 1}, \ + {"wcore", no_argument, &kernel_writablecore, 1}, \ + {"w", no_argument, &kernel_writablecore, 1}, + +#define ADDITIONAL_OPTION_HELP \ + "\ + --kernel Enable kernel debugging.\n\ + --wcore Make core file writable (only works for /dev/mem).\n\ + This option only works while debugging a kernel !!\n\ +" + +extern int kernel_debugging; +extern int kernel_writablecore; + +#define DEFAULT_PROMPT kernel_debugging?"(kgdb) ":"(gdb) " + +/* misuse START_PROGRESS to test whether we're running as kgdb */ +/* START_PROGRESS is called at the top of main */ +#undef START_PROGRESS +#define START_PROGRESS(STR,N) \ + if (!strcmp(STR, "kgdb")) \ + kernel_debugging = 1; + +#include <sys/types.h> +#include <sys/ptrace.h> + +#ifdef PT_GETDBREGS +#define TARGET_HAS_HARDWARE_WATCHPOINTS + +extern int can_watch PARAMS((int type, int cnt, int othertype)); +extern int stopped_by_watchpoint PARAMS((void)); +extern int insert_watchpoint PARAMS((int addr, int len, int type)); +extern int remove_watchpoint PARAMS((int addr, int len, int type)); + +#define TARGET_CAN_USE_HARDWARE_WATCHPOINT(type, cnt, ot) \ + can_watch(type, cnt, ot) + +/* After a watchpoint trap, the PC points to the instruction after + the one that caused the trap. Therefore we don't need to step over it. + But we do need to reset the status register to avoid another trap. */ +#define HAVE_CONTINUABLE_WATCHPOINT + +#define STOPPED_BY_WATCHPOINT(W) \ + stopped_by_watchpoint() + +/* Use these macros for watchpoint insertion/removal. */ + +#define target_insert_watchpoint(addr, len, type) \ + insert_watchpoint(addr, len, type) + +#define target_remove_watchpoint(addr, len, type) \ + remove_watchpoint(addr, len, type) + +#endif /* PT_GETDBREGS */ + +#endif /* NM_FREEBSD_H */ diff --git a/gnu/usr.bin/binutils/gdb5/i386/tm.h b/gnu/usr.bin/binutils/gdb5/i386/tm.h new file mode 100644 index 0000000..69dfa44 --- /dev/null +++ b/gnu/usr.bin/binutils/gdb5/i386/tm.h @@ -0,0 +1,66 @@ +/* $FreeBSD$ */ +/* Target macro definitions for i386 running FreeBSD + Copyright (C) 1997 Free Software Foundation, Inc. + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef TM_FBSD_H +#define TM_FBSD_H 1 + +#include "i386/tm-i386bsd.h" + +#undef NUM_REGS +#define NUM_REGS 16 + +extern struct frame_info *setup_arbitrary_frame PARAMS ((int, CORE_ADDR *)); + +#define SETUP_ARBITRARY_FRAME(argc, argv) setup_arbitrary_frame (argc, argv) + +extern void i386_float_info PARAMS ((void)); + +#define FLOAT_INFO i386_float_info () + +#define IN_SOLIB_CALL_TRAMPOLINE(pc, name) STREQ (name, "_DYNAMIC") + +/* Saved Pc. Get it from sigcontext if within sigtramp. */ + +extern CORE_ADDR fbsd_kern_frame_saved_pc (struct frame_info *); +#undef FRAME_SAVED_PC +#define FRAME_SAVED_PC(FRAME) \ + (kernel_debugging ? fbsd_kern_frame_saved_pc(FRAME) : \ + (((FRAME)->signal_handler_caller \ + ? sigtramp_saved_pc (FRAME) \ + : read_memory_integer ((FRAME)->frame + 4, 4)) \ + )) + +/* On FreeBSD, sigtramp has size 0x18 and is immediately below the + ps_strings struct which has size 0x10 and is at the top of the + user stack. */ + +#undef SIGTRAMP_START +#undef SIGTRAMP_END +#define SIGTRAMP_START(pc) 0xbfbfdfd8 +#define SIGTRAMP_END(pc) 0xbfbfdff0 + +struct objfile; +void freebsd_uthread_new_objfile PARAMS ((struct objfile *objfile)); +#define target_new_objfile(OBJFILE) freebsd_uthread_new_objfile (OBJFILE) + +extern char *freebsd_uthread_pid_to_str PARAMS ((int pid)); +#define target_pid_to_str(PID) freebsd_uthread_pid_to_str (PID) + +#endif /* ifndef TM_FBSD_H */ diff --git a/gnu/usr.bin/binutils/gdb5/i386/version.c b/gnu/usr.bin/binutils/gdb5/i386/version.c new file mode 100644 index 0000000..ea043bd --- /dev/null +++ b/gnu/usr.bin/binutils/gdb5/i386/version.c @@ -0,0 +1,5 @@ +/* $FreeBSD$ */ + +char *version = "4.18"; +char *host_name = "i386-unknown-freebsd"; +char *target_name = "i386-unknown-freebsd"; diff --git a/gnu/usr.bin/binutils/gdb5/i386/xm.h b/gnu/usr.bin/binutils/gdb5/i386/xm.h new file mode 100644 index 0000000..6ff268d --- /dev/null +++ b/gnu/usr.bin/binutils/gdb5/i386/xm.h @@ -0,0 +1,25 @@ +/* Host-dependent definitions for Intel 386 running BSD Unix, for GDB. + Copyright 1986, 1987, 1989, 1992 Free Software Foundation, Inc. + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 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. */ + +/* $FreeBSD$ */ + +#define HOST_BYTE_ORDER LITTLE_ENDIAN + +#include <machine/limits.h> /* for INT_MIN, to avoid "INT_MIN + redefined" warnings from defs.h */ diff --git a/gnu/usr.bin/binutils/gdbreplay5/Makefile b/gnu/usr.bin/binutils/gdbreplay5/Makefile new file mode 100644 index 0000000..6028edc --- /dev/null +++ b/gnu/usr.bin/binutils/gdbreplay5/Makefile @@ -0,0 +1,23 @@ +# $FreeBSD$ + +.include "../Makefile.inc0" + +# Not elf specific so don't install in /usr/libexec/elf +BINDIR=/usr/bin + +GDBDIR= ${.CURDIR}/../../../../contrib/gdb.291 +.PATH: ${GDBDIR}/gdb/gdbserver +.PATH: ${GDBDIR}/gdb + +PROG= gdbreplay +NOMAN= yes + +SRCS= gdbreplay.c + +CFLAGS+= -I${.CURDIR}/../gdb/${MACHINE_ARCH} +CFLAGS+= -I${GDBDIR}/gdb +CFLAGS+= -I${GDBDIR}/gdb/config +CFLAGS+= -I${GDBDIR}/gdb/gdbserver +CFLAGS+= -DNO_MMALLOC + +.include <bsd.prog.mk> diff --git a/gnu/usr.bin/binutils/gdbserver5/Makefile b/gnu/usr.bin/binutils/gdbserver5/Makefile new file mode 100644 index 0000000..c31ba6d --- /dev/null +++ b/gnu/usr.bin/binutils/gdbserver5/Makefile @@ -0,0 +1,23 @@ +# $FreeBSD$ + +.include "../Makefile.inc0" + +# Not elf specific so don't install in /usr/libexec/elf +BINDIR=/usr/bin + +GDBDIR= ${.CURDIR}/../../../../contrib/gdb.291 +.PATH: ${GDBDIR}/gdb/gdbserver +.PATH: ${GDBDIR}/gdb + +PROG= gdbserver + +SRCS= remote-utils.c utils.c server.c +SRCS+= low-fbsd.c + +CFLAGS+= -I${.CURDIR}/../gdb/${MACHINE_ARCH} +CFLAGS+= -I${GDBDIR}/gdb +CFLAGS+= -I${GDBDIR}/gdb/config +CFLAGS+= -I${GDBDIR}/gdb/gdbserver +CFLAGS+= -DNO_MMALLOC + +.include <bsd.prog.mk> diff --git a/gnu/usr.bin/binutils/gdbserver5/low-fbsd.c b/gnu/usr.bin/binutils/gdbserver5/low-fbsd.c new file mode 100644 index 0000000..2f1af94 --- /dev/null +++ b/gnu/usr.bin/binutils/gdbserver5/low-fbsd.c @@ -0,0 +1,341 @@ +/* Low level interface to ptrace, for the remote server for GDB. + Copyright (C) 1995 Free Software Foundation, Inc. + +This file is part of GDB. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 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. */ + +/* $FreeBSD$ */ + +#include "defs.h" +#include <sys/wait.h> +#include "frame.h" +#include "inferior.h" + +#include <stdio.h> +#include <sys/param.h> +#include <dirent.h> +#include <sys/user.h> +#include <signal.h> +#include <sys/ioctl.h> +#include <sgtty.h> +#include <fcntl.h> +#include <string.h> + +/***************Begin MY defs*********************/ +int quit_flag = 0; +char registers[REGISTER_BYTES]; + +/* Index within `registers' of the first byte of the space for + register N. */ + + +char buf2[MAX_REGISTER_RAW_SIZE]; +/***************End MY defs*********************/ + +#include <sys/ptrace.h> +#include <machine/reg.h> + +extern char **environ; +extern int inferior_pid; +void quit (), perror_with_name (); +int query (); + +/* Start an inferior process and returns its pid. + ALLARGS is a vector of program-name and args. + ENV is the environment vector to pass. */ + +int +create_inferior (program, allargs) + char *program; + char **allargs; +{ + int pid; + + pid = fork (); + if (pid < 0) + perror_with_name ("fork"); + + if (pid == 0) + { + ptrace (PT_TRACE_ME, 0, 0, 0); + + execv (program, allargs); + + fprintf (stderr, "Cannot exec %s: %s.\n", program, strerror(errno)); + fflush (stderr); + _exit (0177); + } + + return pid; +} + +/* Kill the inferior process. Make us have no inferior. */ + +void +kill_inferior () +{ + if (inferior_pid == 0) + return; + ptrace (PT_KILL, inferior_pid, 0, 0); + wait (0); + /*************inferior_died ();****VK**************/ +} + +/* Return nonzero if the given thread is still alive. */ +int +mythread_alive (pid) + int pid; +{ + return 1; +} + +/* Wait for process, returns status */ + +unsigned char +mywait (status) + char *status; +{ + int pid; + int w; + + pid = wait (&w); + if (pid != inferior_pid) + perror_with_name ("wait"); + + if (WIFEXITED (w)) + { + fprintf (stderr, "\nChild exited with retcode = %x \n", WEXITSTATUS (w)); + *status = 'W'; + return ((unsigned char) WEXITSTATUS (w)); + } + else if (!WIFSTOPPED (w)) + { + fprintf (stderr, "\nChild terminated with signal = %x \n", WTERMSIG (w)); + *status = 'X'; + return ((unsigned char) WTERMSIG (w)); + } + + fetch_inferior_registers (0); + + *status = 'T'; + return ((unsigned char) WSTOPSIG (w)); +} + +/* Resume execution of the inferior process. + If STEP is nonzero, single-step it. + If SIGNAL is nonzero, give it that signal. */ + +void +myresume (step, signal) + int step; + int signal; +{ + errno = 0; + ptrace (step ? PT_STEP : PT_CONTINUE, inferior_pid, + (PTRACE_ARG3_TYPE) 1, signal); + if (errno) + perror_with_name ("ptrace"); +} + +#if defined(__i386__) + +/* this table must line up with REGISTER_NAMES in tm-i386v.h */ +/* symbols like 'tEAX' come from <machine/reg.h> */ +static int tregmap[] = +{ + tEAX, tECX, tEDX, tEBX, + tESP, tEBP, tESI, tEDI, + tEIP, tEFLAGS, tCS, tSS, + tDS, tES, tFS, tGS, +}; + +static struct save87 pcb_savefpu; + +void +fetch_inferior_registers (regno) + int regno; +{ + struct reg inferior_registers; /* ptrace order, not gcc/gdb order */ + int r; + + ptrace (PT_GETREGS, inferior_pid, + (PTRACE_ARG3_TYPE) &inferior_registers, 0); + + for (r = 0; r < NUM_REGS; r++) + memcpy (®isters[REGISTER_BYTE (r)], ((int *)&inferior_registers) + tregmap[r], 4); +} + +void +store_inferior_registers (regno) + int regno; +{ + struct reg inferior_registers; /* ptrace order, not gcc/gdb order */ + int r; + + ptrace (PT_GETREGS, inferior_pid, + (PTRACE_ARG3_TYPE) &inferior_registers, 0); + + for (r = 0; r < NUM_REGS; r++) + memcpy (((int *)&inferior_registers) + tregmap[r], ®isters[REGISTER_BYTE (r)], 4); + + ptrace (PT_SETREGS, inferior_pid, + (PTRACE_ARG3_TYPE) &inferior_registers, 0); +} + +#elif defined(__alpha__) + +void +fetch_inferior_registers (regno) + int regno; +{ + struct reg regs; /* ptrace order, not gcc/gdb order */ + struct fpreg fpregs; + int r; + + ptrace (PT_GETREGS, inferior_pid, (PTRACE_ARG3_TYPE) ®s, 0); + ptrace (PT_GETFPREGS, inferior_pid, (PTRACE_ARG3_TYPE) &fpregs, 0); + + for (r = 0; r < 31; r++) + memcpy (®isters[REGISTER_BYTE (r)], + ®s.r_regs[r], sizeof(u_int64_t)); + for (r = 0; r < 32; r++) + memcpy (®isters[REGISTER_BYTE (r + FP0_REGNUM)], + &fpregs.fpr_regs[r], sizeof(u_int64_t)); + memcpy (®isters[REGISTER_BYTE (PC_REGNUM)], + ®s.r_regs[31], sizeof(u_int64_t)); + + memset (®isters[REGISTER_BYTE (ZERO_REGNUM)], 0, sizeof(u_int64_t)); + memset (®isters[REGISTER_BYTE (FP_REGNUM)], 0, sizeof(u_int64_t)); +} + +void +store_inferior_registers (regno) + int regno; +{ + struct reg regs; /* ptrace order, not gcc/gdb order */ + struct fpreg fpregs; + int r; + + for (r = 0; r < 31; r++) + memcpy (®s.r_regs[r], + ®isters[REGISTER_BYTE (r)], sizeof(u_int64_t)); + for (r = 0; r < 32; r++) + memcpy (&fpregs.fpr_regs[r], + ®isters[REGISTER_BYTE (r + FP0_REGNUM)], sizeof(u_int64_t)); + memcpy (®s.r_regs[31], + ®isters[REGISTER_BYTE (PC_REGNUM)], sizeof(u_int64_t)); + + ptrace (PT_SETREGS, inferior_pid, (PTRACE_ARG3_TYPE) ®s, 0); + ptrace (PT_SETFPREGS, inferior_pid, (PTRACE_ARG3_TYPE) &fpregs, 0); +} + +#endif + + +/* NOTE! I tried using PTRACE_READDATA, etc., to read and write memory + in the NEW_SUN_PTRACE case. + It ought to be straightforward. But it appears that writing did + not write the data that I specified. I cannot understand where + it got the data that it actually did write. */ + +/* Copy LEN bytes from inferior's memory starting at MEMADDR + to debugger memory starting at MYADDR. */ + +read_inferior_memory (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + register int i; + /* Round starting address down to longword boundary. */ + register CORE_ADDR addr = memaddr & -sizeof (int); + /* Round ending address up; get number of longwords that makes. */ + register int count + = (((memaddr + len) - addr) + sizeof (int) - 1) / sizeof (int); + /* Allocate buffer of that many longwords. */ + register int *buffer = (int *) alloca (count * sizeof (int)); + + /* Read all the longwords */ + for (i = 0; i < count; i++, addr += sizeof (int)) + { + buffer[i] = ptrace (PT_READ_I, inferior_pid, (PTRACE_ARG3_TYPE) addr, 0); + } + + /* Copy appropriate bytes out of the buffer. */ + memcpy (myaddr, (char *) buffer + (memaddr & (sizeof (int) - 1)), len); +} + +/* Copy LEN bytes of data from debugger memory at MYADDR + to inferior's memory at MEMADDR. + On failure (cannot write the inferior) + returns the value of errno. */ + +int +write_inferior_memory (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + register int i; + /* Round starting address down to longword boundary. */ + register CORE_ADDR addr = memaddr & -sizeof (int); + /* Round ending address up; get number of longwords that makes. */ + register int count + = (((memaddr + len) - addr) + sizeof (int) - 1) / sizeof (int); + /* Allocate buffer of that many longwords. */ + register int *buffer = (int *) alloca (count * sizeof (int)); + extern int errno; + + /* Fill start and end extra bytes of buffer with existing memory data. */ + + buffer[0] = ptrace (PT_READ_I, inferior_pid, + (PTRACE_ARG3_TYPE) addr, 0); + + if (count > 1) + { + buffer[count - 1] + = ptrace (PT_READ_I, inferior_pid, + (PTRACE_ARG3_TYPE) addr + (count - 1) * sizeof (int), 0); + } + + /* Copy data to be written over corresponding part of buffer */ + + memcpy ((char *) buffer + (memaddr & (sizeof (int) - 1)), myaddr, len); + + /* Write the entire buffer. */ + + for (i = 0; i < count; i++, addr += sizeof (int)) + { + errno = 0; + ptrace (PT_WRITE_I, inferior_pid, (PTRACE_ARG3_TYPE) addr, buffer[i]); + if (errno) + return errno; + } + + return 0; +} + +void +initialize () +{ + inferior_pid = 0; +} + +int +have_inferior_p () +{ + return inferior_pid != 0; +} |