diff options
Diffstat (limited to 'gdb/i386-linux-nat.c')
-rw-r--r-- | gdb/i386-linux-nat.c | 379 |
1 files changed, 379 insertions, 0 deletions
diff --git a/gdb/i386-linux-nat.c b/gdb/i386-linux-nat.c new file mode 100644 index 0000000..8c63a94 --- /dev/null +++ b/gdb/i386-linux-nat.c @@ -0,0 +1,379 @@ +/* Native-dependent code for Linux running on i386's, for GDB. + +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. */ + +#include "defs.h" +#include "inferior.h" +#include "gdbcore.h" + +/* For i386_linux_skip_solib_resolver */ +#include "symtab.h" +#include "frame.h" +#include "symfile.h" +#include "objfiles.h" + +#include <sys/ptrace.h> +#include <sys/user.h> +#include <sys/procfs.h> + +#ifdef HAVE_SYS_REG_H +#include <sys/reg.h> +#endif + +/* This is a duplicate of the table in i386-xdep.c. */ + +static int regmap[] = +{ + EAX, ECX, EDX, EBX, + UESP, EBP, ESI, EDI, + EIP, EFL, CS, SS, + DS, ES, FS, GS, +}; + + +/* FIXME: These routine absolutely depends upon (NUM_REGS - NUM_FREGS) + being less than or equal to the number of registers that can be stored + in a gregset_t. Note that with the current scheme there will typically + be more registers actually stored in a gregset_t that what we know + about. This is bogus and should be fixed. */ + +/* Given a pointer to a general register set in /proc format (gregset_t *), + unpack the register contents and supply them as gdb's idea of the current + register values. */ + +void +supply_gregset (gregsetp) + gregset_t *gregsetp; +{ + register int regi; + register greg_t *regp = (greg_t *) gregsetp; + + for (regi = 0 ; regi < (NUM_REGS - NUM_FREGS) ; regi++) + { + supply_register (regi, (char *) (regp + regmap[regi])); + } +} + +void +fill_gregset (gregsetp, regno) + gregset_t *gregsetp; + int regno; +{ + int regi; + register greg_t *regp = (greg_t *) gregsetp; + + for (regi = 0 ; regi < (NUM_REGS - NUM_FREGS) ; regi++) + { + if ((regno == -1) || (regno == regi)) + { + *(regp + regmap[regi]) = *(int *) ®isters[REGISTER_BYTE (regi)]; + } + } +} + + +/* Given a pointer to a floating point register set in (fpregset_t *) + format, unpack the register contents and supply them as gdb's + idea of the current floating point register values. */ + +void +supply_fpregset (fpregsetp) + fpregset_t *fpregsetp; +{ + register int regi; + char *from; + from = (char *) &(fpregsetp->st_space[0]); + for (regi = FPSTART_REGNUM ; regi <= FPEND_REGNUM ; regi++) + { + supply_register(regi, from); + from += REGISTER_RAW_SIZE(regi); + } +} + +/* Given a pointer to a floating point register set in (fpregset_t *) + format, update all of the registers from gdb's idea + of the current floating point register set. */ + +void +fill_fpregset (fpregsetp, regno) + fpregset_t *fpregsetp; + int regno; +{ + int regi; + char *to; + char *from; + + to = (char *) &(fpregsetp->st_space[0]); + for (regi = FPSTART_REGNUM ; regi <= FPEND_REGNUM ; regi++) + { + from = (char *) ®isters[REGISTER_BYTE (regi)]; + memcpy (to, from, REGISTER_RAW_SIZE (regi)); + to += REGISTER_RAW_SIZE(regi); + } +} + +/* + Get the whole floating point state of the process and + store the floating point stack into registers[]. + */ +static void +fetch_fpregs(void) +{ + int ret, regno; + char buf[FPREG_BYTES]; + + ret = ptrace (PTRACE_GETFPREGS, inferior_pid, 0, (int)buf); + if ( ret < 0 ) + { + warning ("Couldn't get floating point status"); + return; + } + + for ( regno = 0; regno < NUM_FREGS; regno++ ) + { + if ( regno < 7 ) + supply_register (NUM_REGS-NUM_FREGS+regno, buf + regno*4); + else + supply_register (NUM_REGS-NUM_FREGS+regno, + buf + FPENV_BYTES + (regno-7)*FPREG_RAW_SIZE); + } + +} + + +/* + Get the whole floating point state of the process and + replace the contents from registers[]. + */ +static void +store_fpregs(void) +{ + int ret, regno; + char buf[FPREG_BYTES]; + + ret = ptrace (PTRACE_GETFPREGS, inferior_pid, 0, (int)buf); + if ( ret < 0 ) + { + warning ("Couldn't get floating point status"); + return; + } + + for ( regno = 0; regno < NUM_FREGS; regno++ ) + { + if ( register_valid[regno] ) + { + if ( regno < 7 ) + { + read_register_gen (NUM_REGS-NUM_FREGS+regno, + buf + regno*4); + } + else + { + read_register_gen (NUM_REGS-NUM_FREGS+regno, + buf + FPENV_BYTES + (regno-7)*FPREG_RAW_SIZE); + } + } + } + + ret = ptrace (PTRACE_SETFPREGS, inferior_pid, 0, (int)buf); + if ( ret < 0 ) + { + warning ("Couldn't write floating point status"); + return; + } + +} + + +/* + Get state of all non-fp registers of the process and + store into registers[]. + */ +static void +fetch_regs(void) +{ + int ret, regno; + char buf[17*sizeof(unsigned int)]; + + ret = ptrace (PTRACE_GETREGS, inferior_pid, 0, (int)buf); + if ( ret < 0 ) + { + warning ("Couldn't get registers"); + return; + } + + for ( regno = 0; regno < NUM_REGS-NUM_FREGS; regno++ ) + supply_register (regno, buf + register_addr (regno, U_REGS_OFFSET)); + +} + + +/* + Get the whole non-floating-point register state of the process and + replace them in the process from registers[]. + */ +static void +store_regs(void) +{ + int ret, regno; + char buf[17*sizeof(unsigned int)]; + + ret = ptrace (PTRACE_GETREGS, inferior_pid, 0, (int)buf); + if ( ret < 0 ) + { + warning ("Couldn't get registers"); + return; + } + + for ( regno = 0; regno < NUM_REGS-NUM_FREGS; regno++ ) + { + if ( register_valid[regno] ) + read_register_gen (regno, buf + register_addr (regno, U_REGS_OFFSET)); + } + + ret = ptrace (PTRACE_SETREGS, inferior_pid, 0, (int)buf); + + if ( ret < 0 ) + { + warning ("Couldn't write floating point status"); + return; + } + +} + + +/* Fetch registers from the child process. + Fetch all if regno == -1, otherwise fetch all ordinary + registers or all floating point registers depending + upon the value of regno. */ + +void +fetch_inferior_registers (regno) + int regno; +{ + if ( (regno < NUM_REGS - NUM_FREGS) || (regno == -1) ) + fetch_regs(); + + if ( (regno >= NUM_REGS - NUM_FREGS) || (regno == -1) ) + fetch_fpregs(); +} + + +/* Store our register values back into the inferior. + If REGNO is -1, do this for all registers. + Otherwise, REGNO specifies which register, which + then determines whether we store all ordinary + registers or all of the floating point registers. */ + +void +store_inferior_registers (regno) + int regno; +{ + if ( (regno < NUM_REGS - NUM_FREGS) || (regno == -1) ) + store_regs(); + + if ( (regno >= NUM_REGS - NUM_FREGS) || (regno == -1) ) + store_fpregs(); +} + + +/* Find the minimal symbol named NAME, and return both the minsym + struct and its objfile. This probably ought to be in minsym.c, but + everything there is trying to deal with things like C++ and + SOFUN_ADDRESS_MAYBE_TURQUOISE, ... Since this is so simple, it may + be considered too special-purpose for general consumption. */ + +static struct minimal_symbol * +find_minsym_and_objfile (char *name, struct objfile **objfile_p) +{ + struct objfile *objfile; + + ALL_OBJFILES (objfile) + { + struct minimal_symbol *msym; + + ALL_OBJFILE_MSYMBOLS (objfile, msym) + { + if (SYMBOL_NAME (msym) + && STREQ (SYMBOL_NAME (msym), name)) + { + *objfile_p = objfile; + return msym; + } + } + } + + return 0; +} + + +static CORE_ADDR +skip_hurd_resolver (CORE_ADDR pc) +{ + /* The HURD dynamic linker is part of the GNU C library, so many + GNU/Linux distributions use it. (All ELF versions, as far as I + know.) An unresolved PLT entry points to "_dl_runtime_resolve", + which calls "fixup" to patch the PLT, and then passes control to + the function. + + We look for the symbol `_dl_runtime_resolve', and find `fixup' in + the same objfile. If we are at the entry point of `fixup', then + we set a breakpoint at the return address (at the top of the + stack), and continue. + + It's kind of gross to do all these checks every time we're + called, since they don't change once the executable has gotten + started. But this is only a temporary hack --- upcoming versions + of Linux will provide a portable, efficient interface for + debugging programs that use shared libraries. */ + + struct objfile *objfile; + struct minimal_symbol *resolver + = find_minsym_and_objfile ("_dl_runtime_resolve", &objfile); + + if (resolver) + { + struct minimal_symbol *fixup + = lookup_minimal_symbol ("fixup", 0, objfile); + + if (fixup && SYMBOL_VALUE_ADDRESS (fixup) == pc) + return (SAVED_PC_AFTER_CALL (get_current_frame ())); + } + + return 0; +} + + +/* See the comments for SKIP_SOLIB_RESOLVER at the top of infrun.c. + This function: + 1) decides whether a PLT has sent us into the linker to resolve + a function reference, and + 2) if so, tells us where to set a temporary breakpoint that will + trigger when the dynamic linker is done. */ + +CORE_ADDR +i386_linux_skip_solib_resolver (CORE_ADDR pc) +{ + CORE_ADDR result; + + /* Plug in functions for other kinds of resolvers here. */ + result = skip_hurd_resolver (pc); + if (result) + return result; + + return 0; +} |