From 3547b75e970245fcb25550f6fd8036183002b222 Mon Sep 17 00:00:00 2001 From: "David E. O'Brien" Date: Sun, 25 Mar 2001 02:20:37 +0000 Subject: [PATCH] Temporary build structure for GDB 5.0 so people can test the new version before pulling the switch to making it the default version. --- gnu/usr.bin/binutils/gdb5/Makefile | 115 ++ gnu/usr.bin/binutils/gdb5/Makefile.alpha | 8 + gnu/usr.bin/binutils/gdb5/Makefile.i386 | 7 + gnu/usr.bin/binutils/gdb5/Makefile.ia64 | 8 + gnu/usr.bin/binutils/gdb5/alpha/freebsd-nat.c | 175 +++ gnu/usr.bin/binutils/gdb5/alpha/kvm-fbsd.c | 438 +++++++ gnu/usr.bin/binutils/gdb5/alpha/nm.h | 96 ++ gnu/usr.bin/binutils/gdb5/alpha/tm.h | 45 + gnu/usr.bin/binutils/gdb5/alpha/version.c | 5 + gnu/usr.bin/binutils/gdb5/alpha/xm.h | 32 + gnu/usr.bin/binutils/gdb5/config.h | 328 +++++ gnu/usr.bin/binutils/gdb5/freebsd-uthread.c | 1139 +++++++++++++++++ gnu/usr.bin/binutils/gdb5/i386/freebsd-nat.c | 670 ++++++++++ gnu/usr.bin/binutils/gdb5/i386/kvm-fbsd.c | 995 ++++++++++++++ gnu/usr.bin/binutils/gdb5/i386/nm.h | 170 +++ gnu/usr.bin/binutils/gdb5/i386/tm.h | 66 + gnu/usr.bin/binutils/gdb5/i386/version.c | 5 + gnu/usr.bin/binutils/gdb5/i386/xm.h | 25 + gnu/usr.bin/binutils/gdbreplay5/Makefile | 23 + gnu/usr.bin/binutils/gdbserver5/Makefile | 23 + gnu/usr.bin/binutils/gdbserver5/low-fbsd.c | 341 +++++ 21 files changed, 4714 insertions(+) create mode 100644 gnu/usr.bin/binutils/gdb5/Makefile create mode 100644 gnu/usr.bin/binutils/gdb5/Makefile.alpha create mode 100644 gnu/usr.bin/binutils/gdb5/Makefile.i386 create mode 100644 gnu/usr.bin/binutils/gdb5/Makefile.ia64 create mode 100644 gnu/usr.bin/binutils/gdb5/alpha/freebsd-nat.c create mode 100644 gnu/usr.bin/binutils/gdb5/alpha/kvm-fbsd.c create mode 100644 gnu/usr.bin/binutils/gdb5/alpha/nm.h create mode 100644 gnu/usr.bin/binutils/gdb5/alpha/tm.h create mode 100644 gnu/usr.bin/binutils/gdb5/alpha/version.c create mode 100644 gnu/usr.bin/binutils/gdb5/alpha/xm.h create mode 100644 gnu/usr.bin/binutils/gdb5/config.h create mode 100644 gnu/usr.bin/binutils/gdb5/freebsd-uthread.c create mode 100644 gnu/usr.bin/binutils/gdb5/i386/freebsd-nat.c create mode 100644 gnu/usr.bin/binutils/gdb5/i386/kvm-fbsd.c create mode 100644 gnu/usr.bin/binutils/gdb5/i386/nm.h create mode 100644 gnu/usr.bin/binutils/gdb5/i386/tm.h create mode 100644 gnu/usr.bin/binutils/gdb5/i386/version.c create mode 100644 gnu/usr.bin/binutils/gdb5/i386/xm.h create mode 100644 gnu/usr.bin/binutils/gdbreplay5/Makefile create mode 100644 gnu/usr.bin/binutils/gdbserver5/Makefile create mode 100644 gnu/usr.bin/binutils/gdbserver5/low-fbsd.c diff --git a/gnu/usr.bin/binutils/gdb5/Makefile b/gnu/usr.bin/binutils/gdb5/Makefile new file mode 100644 index 000000000000..b7be7006b7d0 --- /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 diff --git a/gnu/usr.bin/binutils/gdb5/Makefile.alpha b/gnu/usr.bin/binutils/gdb5/Makefile.alpha new file mode 100644 index 000000000000..bb111056e19a --- /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 000000000000..607463bded0c --- /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 000000000000..160e966bbed8 --- /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 000000000000..4bfd8ed3cb83 --- /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 +#include +#include +#include +#include +#include +#include +#include "gdbcore.h" +#include "value.h" +#include "inferior.h" + +#if defined(HAVE_GREGSET_T) +#include +#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 , 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 000000000000..6da1a8e455cf --- /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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 000000000000..4bc1dea2c0af --- /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 000000000000..596a0ef0276b --- /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 000000000000..84b5d88146f7 --- /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 000000000000..1bd0118dff44 --- /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 diff --git a/gnu/usr.bin/binutils/gdb5/config.h b/gnu/usr.bin/binutils/gdb5/config.h new file mode 100644 index 000000000000..eb6ff3cf9e82 --- /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 is included. */ +/* #undef NEED_DECLARATION_MALLOC */ + +/* Whether realloc must be declared even if is included. */ +/* #undef NEED_DECLARATION_REALLOC */ + +/* Whether free must be declared even if is included. */ +/* #undef NEED_DECLARATION_FREE */ + +/* Whether strerror must be declared even if 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 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 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 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 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 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 header file. */ +/* #undef HAVE_ARGZ_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_ASM_DEBUGREG_H */ + +/* Define if you have the header file. */ +#define HAVE_CTYPE_H 1 + +/* Define if you have the header file. */ +#define HAVE_CURSES_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_ENDIAN_H */ + +/* Define if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define if you have the header file. */ +#define HAVE_LINK_H 1 + +/* Define if you have the header file. */ +#define HAVE_LOCALE_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_MALLOC_H */ + +/* Define if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define if you have the header file. */ +#define HAVE_NL_TYPES_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_OBJLIST_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_PTRACE_H */ + +/* Define if you have the header file. */ +#define HAVE_SGTTY_H 1 + +/* Define if you have the header file. */ +#define HAVE_STDDEF_H 1 + +/* Define if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_DEBUGREG_H */ + +/* Define if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_PROCFS_H 1 + +/* Define if you have the header file. */ +#define HAVE_SYS_PTRACE_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_SYS_REG_H */ + +/* Define if you have the header file. */ +#define HAVE_SYS_WAIT_H 1 + +/* Define if you have the header file. */ +#define HAVE_TERM_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_TERMIO_H */ + +/* Define if you have the header file. */ +#define HAVE_TERMIOS_H 1 + +/* Define if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define if you have the header file. */ +/* #undef HAVE_VALUES_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_WAIT_H */ + +/* Define if you have the header file. */ +/* #undef HAVE_WCHAR_H */ + +/* Define if you have the 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 has pstatus_t. */ +/* #undef HAVE_PSTATUS_T */ + +/* Define if has prrun_t. */ +/* #undef HAVE_PRRUN_T */ + +/* Define if has gregset_t. */ +#define HAVE_GREGSET_T 1 + +/* Define if 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 000000000000..1c1ea78f01d6 --- /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 +#include +#include +#include "gdbthread.h" +#include "target.h" +#include "inferior.h" +#include +#include +#include +#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 000000000000..aca4b6dc56ca --- /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 +#include +#include +#include +#include +#include +#include "gdbcore.h" +#include "value.h" +#include "inferior.h" + +#if defined(HAVE_GREGSET_T) || defined(HAVE_FPREGSET_T) +#include +#endif + +/* this table must line up with REGISTER_NAMES in tm-i386v.h */ +/* symbols like 'tEAX' come from */ +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 +#include +#include +#include + +#include + +#include +#include +#include +#define curpcb Xcurpcb /* XXX avoid leaking declaration from pcb.h */ +#include +#undef curpcb +#include +#include "gdb_stat.h" +#include + +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 */ + 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 000000000000..9f2d69d4b879 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#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 +#include +#include +#include + +#include +#include +#include +#include +#include + +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 000000000000..1bf0c3871148 --- /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 +#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 +#include + +#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 000000000000..69dfa44fec92 --- /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 000000000000..ea043bd0bd16 --- /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 000000000000..6ff268dc01d0 --- /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 /* 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 000000000000..6028edce9522 --- /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 diff --git a/gnu/usr.bin/binutils/gdbserver5/Makefile b/gnu/usr.bin/binutils/gdbserver5/Makefile new file mode 100644 index 000000000000..c31ba6d9cdf1 --- /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 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 000000000000..2f1af940838f --- /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 +#include "frame.h" +#include "inferior.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/***************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 +#include + +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 */ +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; +}