/* Native-dependent code for Interix running on i386's, for GDB. Copyright 2002 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. */ #include "defs.h" #include #include #include #include #include #include #include "symfile.h" #include "objfiles.h" #include "gdb-stabs.h" #include "gdbcore.h" #include "gregset.h" typedef unsigned long greg_t; /* Where should this be defined? */ /* See procfs.c and *interix*.h in config/[alpha,i386] */ /* The /proc interface traditionally divides the target machine's register set up into two different sets, the general register set (gregset) and the floating point register set (fpregset). This is reflected in the data structures in . However, the read/write /proc for interix treats them all as part of the status subfile. These routines provide the packing and unpacking of gregset_t and fpregset_t formatted data. */ /* 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, }; /* 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->gregs; for (regi = 0; regi < I386_NUM_GREGS; regi++) { supply_register (regi, (char *) (regp + regmap[regi])); } } /* Fill in a gregset_t object with selected data from a gdb-format register file. - GREGSETP points to the gregset_t object to be filled. - GDB_REGS points to the GDB-style register file providing the data. - VALID is an array indicating which registers in GDB_REGS are valid; the parts of *GREGSETP that would hold registers marked invalid in GDB_REGS are left unchanged. If VALID is zero, all registers are assumed to be valid. */ void convert_to_gregset (gregset_t *gregsetp, char *gdb_regs, signed char *valid) { int regi; register greg_t *regp = (greg_t *) gregsetp->gregs; for (regi = 0; regi < I386_NUM_GREGS; regi++) if (! valid || valid[regi]) *(regp + regmap[regi]) = * (int *) ®isters[REGISTER_BYTE (regi)]; } /* Store GDB's value for REGNO in *GREGSETP. If REGNO is -1, do all of them. */ void fill_gregset (gregset_t *gregsetp, int regno) { if (regno == -1) convert_to_gregset (gregsetp, registers, 0); else if (regno >= 0 && regno < I386_NUM_GREGS) { signed char valid[I386_NUM_GREGS]; memset (valid, 0, sizeof (valid)); valid[regno] = 1; convert_to_gregset (gregsetp, registers, valid); } } /* Given a pointer to a floating point register set in /proc format (fpregset_t *), unpack the register contents and supply them as gdb's idea of the current floating point register values. */ /* What is the address of st(N) within the fpregset_t structure F? */ #define FPREGSET_T_FPREG_ADDR(f, n) \ ((char *) &(f)->fpstate.fpregs + (n) * 10) /* Fill GDB's register file with the floating-point register values in *FPREGSETP. */ void supply_fpregset (fpregset_t *fpregsetp) { int i; long l; /* Supply the floating-point registers. */ for (i = 0; i < 8; i++) supply_register (FP0_REGNUM + i, FPREGSET_T_FPREG_ADDR (fpregsetp, i)); l = fpregsetp->fpstate.control & 0xffff; supply_register (FCTRL_REGNUM, (char *) &l); l = fpregsetp->fpstate.status & 0xffff; supply_register (FSTAT_REGNUM, (char *) &l); l = fpregsetp->fpstate.tag & 0xffff; supply_register (FTAG_REGNUM, (char *) &l); supply_register (FCOFF_REGNUM, (char *) &fpregsetp->fpstate.erroroffset); l = fpregsetp->fpstate.dataselector & 0xffff; supply_register (FDS_REGNUM, (char *) &l); supply_register (FDOFF_REGNUM, (char *) &fpregsetp->fpstate.dataoffset); /* Extract the code segment and opcode from the "fcs" member. */ l = fpregsetp->fpstate.errorselector & 0xffff; supply_register (FCS_REGNUM, (char *) &l); l = (fpregsetp->fpstate.errorselector >> 16) & ((1 << 11) - 1); supply_register (FOP_REGNUM, (char *) &l); } /* Fill in an fpregset_t structure with selected data from a gdb-format register file. - FPREGSETP points to the structure to be filled. - GDB_REGS points to the GDB-style register file providing the data. - VALID is an array indicating which registers in GDB_REGS are valid; the parts of *FPREGSETP that would hold registers marked invalid in GDB_REGS are left unchanged. If VALID is zero, all registers are assumed to be valid. */ void convert_to_fpregset (fpregset_t *fpregsetp, char *gdb_regs, signed char *valid) { int i; /* Fill in the floating-point registers. */ for (i = 0; i < 8; i++) if (!valid || valid[i]) memcpy (FPREGSET_T_FPREG_ADDR (fpregsetp, i), ®isters[REGISTER_BYTE (FP0_REGNUM + i)], REGISTER_RAW_SIZE(FP0_REGNUM + i)); #define fill(MEMBER, REGNO) \ if (! valid || valid[(REGNO)]) \ memcpy (&fpregsetp->fpstate.MEMBER, ®isters[REGISTER_BYTE (REGNO)], \ sizeof (fpregsetp->fpstate.MEMBER)) fill (control, FCTRL_REGNUM); fill (status, FSTAT_REGNUM); fill (tag, FTAG_REGNUM); fill (erroroffset, FCOFF_REGNUM); fill (dataoffset, FDOFF_REGNUM); fill (dataselector, FDS_REGNUM); #undef fill if (! valid || valid[FCS_REGNUM]) fpregsetp->fpstate.errorselector = ((fpregsetp->fpstate.errorselector & ~0xffff) | (* (int *) ®isters[REGISTER_BYTE (FCS_REGNUM)] & 0xffff)); if (! valid || valid[FOP_REGNUM]) fpregsetp->fpstate.errorselector = ((fpregsetp->fpstate.errorselector & 0xffff) | ((*(int *) ®isters[REGISTER_BYTE (FOP_REGNUM)] & ((1 << 11) - 1)) << 16)); } /* 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 (fpregset_t *fpregsetp, int regno) { convert_to_fpregset (fpregsetp, registers, 0); } /* GLOBAL FUNCTION fetch_core_registers -- fetch current registers from core file SYNOPSIS void fetch_core_registers (char *core_reg_sect, unsigned core_reg_size, int which, CORE_ADDR reg_addr) DESCRIPTION Read the values of either the general register set (WHICH equals 0) or the floating point register set (WHICH equals 2) from the core file data (pointed to by CORE_REG_SECT), and update gdb's idea of their current values. The CORE_REG_SIZE parameter is compared to the size of the gregset or fpgregset structures (as appropriate) to validate the size of the structure from the core file. The REG_ADDR parameter is ignored. */ static void fetch_core_registers (char *core_reg_sect, unsigned core_reg_size, int which, CORE_ADDR reg_addr) { gdb_gregset_t gregset; gdb_fpregset_t fpregset; if (which == 0) { if (core_reg_size != sizeof (gregset)) { warning ("wrong size gregset struct in core file"); } else { memcpy ((char *) &gregset, core_reg_sect, sizeof (gregset)); supply_gregset (&gregset); } } else if (which == 2) { if (core_reg_size != sizeof (fpregset)) { warning ("wrong size fpregset struct in core file"); } else { memcpy ((char *) &fpregset, core_reg_sect, sizeof (fpregset)); if (FP0_REGNUM >= 0) supply_fpregset (&fpregset); } } } /* the name of the proc status struct depends on the implementation */ #ifdef HAVE_PSTATUS_T typedef pstatus_t gdb_prstatus_t; #else typedef prstatus_t gdb_prstatus_t; #endif /* 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 that we will land at. The pc is copied into PC. This routine returns true on success. */ #include int get_longjmp_target (pc) CORE_ADDR *pc; { CORE_ADDR sp, jb_addr; char raw_buffer[MAX_REGISTER_RAW_SIZE]; sp = read_register (SP_REGNUM); if (target_read_memory (sp + SP_ARG0, /* Offset of first arg on stack */ raw_buffer, TARGET_PTR_BIT / TARGET_CHAR_BIT)) return 0; jb_addr = extract_address (raw_buffer, TARGET_PTR_BIT / TARGET_CHAR_BIT); if (target_read_memory(jb_addr + offsetof(_JUMP_BUFFER,Eip), raw_buffer, sizeof(CORE_ADDR))) return 0; *pc = extract_address (raw_buffer, sizeof(CORE_ADDR)); return 1; } static struct core_fns interix_core_fns = { bfd_target_coff_flavour, /* core_flavour (more or less) */ default_check_format, /* check_format */ default_core_sniffer, /* core_sniffer */ fetch_core_registers, /* core_read_registers */ NULL /* next */ }; void _initialize_core_interix (void) { add_core_fns (&interix_core_fns); if (TARGET_LONG_BIT != 32) printf_unfiltered ("ERROR: TARGET_LONG_BIT = %d /= 32!\n", TARGET_LONG_BIT); if (TARGET_LONG_LONG_BIT != 64) printf_unfiltered ("ERROR: TARGET_LONG_LONG_BIT = %d /= 64!\n", TARGET_LONG_LONG_BIT); } /* Adjust the section offsets in an objfile structure so that it's correct for the type of symbols being read (or undo it with the _restore arguments). If main programs ever start showing up at other than the default Image Base, this is where that would likely be applied. */ void pei_adjust_objfile_offsets(struct objfile *objfile, enum objfile_adjusts type) { int i; CORE_ADDR symbols_offset; switch (type) { case adjust_for_symtab: symbols_offset = NONZERO_LINK_BASE(objfile->obfd); break; case adjust_for_symtab_restore: symbols_offset = -NONZERO_LINK_BASE(objfile->obfd); break; case adjust_for_stabs: case adjust_for_stabs_restore: case adjust_for_dwarf: case adjust_for_dwarf_restore: default: return; } for (i = 0; i < SECT_OFF_MAX; i++) { (objfile->section_offsets)->offsets[i] += symbols_offset; } } /* We don't have a /proc/pid/file or /proc/pid/exe to read a link from, so read it from the same place ps gets the name. */ char * child_pid_to_exec_file (int pid) { char *path; char *buf; int fd, c; char *p; asprintf (&path, "/proc/%d/stat", pid); buf = xcalloc (MAXPATHLEN+1, sizeof (char)); make_cleanup (xfree, path); make_cleanup (xfree, buf); fd = open (path, O_RDONLY); if (fd < 0) return NULL; /* Skip over "Argv0\t" */ lseek (fd, 6, SEEK_SET); c = read (fd, buf, MAXPATHLEN); close (fd); if (c < 0) return NULL; buf[c] = '\0'; /* Ensure null termintion. */ p = strchr(buf, '\n'); if (p != NULL) *p = '\0'; return buf; }