| /* Target-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 "arch-utils.h" | 
 |  | 
 | #include "frame.h" | 
 | #include "gdb_string.h" | 
 | #include "gdb-stabs.h" | 
 | #include "gdbcore.h" | 
 | #include "gdbtypes.h" | 
 | #include "i386-tdep.h" | 
 | #include "inferior.h" | 
 | #include "libbfd.h" | 
 | #include "objfiles.h" | 
 | #include "osabi.h" | 
 | #include "regcache.h" | 
 |  | 
 | /* offsetof (mcontext_t, gregs.gregs[EBP]) */ | 
 | static const int mcontext_EBP_greg_offset = 180; | 
 |  | 
 | /* offsetof (mcontext_t, gregs.gregs[EIP]) */ | 
 | static const int mcontext_EIP_greg_offset = 184; | 
 |  | 
 | /* offsetof (mcontext_t, gregs.gregs[UESP]) */ | 
 | static const int mcontext_UESP_greg_offset = 196; | 
 |  | 
 | /* offsetof (mcontext_t, gregs.reserved[1]) */ | 
 | static const int mcontext_syscall_greg_offset = 4; | 
 |  | 
 | /* offsetof (_JUMP_BUFFER, Eip) */ | 
 | static const int jump_buffer_Eip_offset = 20; | 
 |  | 
 | /* See procfs.c and *interix*.h in config/[alpha,i386].  */ | 
 | /* ??? These should be static, but this needs a bit of work before this | 
 |    can be done.  */ | 
 | CORE_ADDR tramp_start; | 
 | CORE_ADDR tramp_end; | 
 | CORE_ADDR null_start; | 
 | CORE_ADDR null_end; | 
 | int winver;                     /* Windows NT version number */ | 
 |  | 
 | /* Forward declarations.  */ | 
 | extern void _initialize_i386_interix_tdep (void); | 
 | extern initialize_file_ftype _initialize_i386_interix_tdep; | 
 |  | 
 | /* 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; | 
 |     } | 
 | } | 
 |  | 
 | static int | 
 | i386_interix_pc_in_sigtramp (CORE_ADDR pc, char *name) | 
 | { | 
 |   /* This is sufficient, where used, but is NOT a complete test; There | 
 |      is more in DEPRECATED_INIT_EXTRA_FRAME_INFO | 
 |      (a.k.a. interix_back_one_frame).  */ | 
 |   return ((pc >= tramp_start && pc < tramp_end) | 
 |           || (pc >= null_start && pc < null_end)); | 
 | } | 
 |  | 
 | static int | 
 | i386_interix_in_solib_call_trampoline (CORE_ADDR pc, char *name) | 
 | { | 
 |   return i386_pe_skip_trampoline_code (pc, name); | 
 | } | 
 |  | 
 | static CORE_ADDR | 
 | i386_interix_skip_trampoline_code (CORE_ADDR pc) | 
 | { | 
 |   return i386_pe_skip_trampoline_code (pc, 0); | 
 | } | 
 |  | 
 | static int | 
 | i386_interix_frame_chain_valid (CORE_ADDR chain, struct frame_info *thisframe) | 
 | { | 
 |   /* In the context where this is used, we get the saved PC before we've | 
 |      successfully unwound far enough to be sure what we've got (it may | 
 |      be a signal handler caller).  If we're dealing with a signal | 
 |      handler caller, this will return valid, which is fine.  If not, | 
 |      it'll make the correct test.  */ | 
 |   return ((get_frame_type (thisframe) == SIGTRAMP_FRAME) | 
 |           || (chain != 0 | 
 |               && !inside_entry_file (read_memory_integer | 
 |                                      (thisframe->frame + 4, 4)))); | 
 | } | 
 |  | 
 | /* We want to find the previous frame, which on Interix is tricky when | 
 |    signals are involved; set frame->frame appropriately, and also get | 
 |    the pc and tweak tye frame's type; this replaces a boatload of | 
 |    nested macros, as well.  */ | 
 | static void | 
 | i386_interix_back_one_frame (int fromleaf, struct frame_info *frame) | 
 | { | 
 |   CORE_ADDR ra; | 
 |   CORE_ADDR fm; | 
 |   CORE_ADDR context; | 
 |   long t; | 
 |  | 
 |   if (frame == NULL) | 
 |     internal_error (__FILE__, __LINE__, "unexpected NULL frame"); | 
 |  | 
 |   if (fromleaf) | 
 |     { | 
 |       frame->pc = SAVED_PC_AFTER_CALL (frame->next); | 
 |       return; | 
 |     } | 
 |  | 
 |   if (!frame->next) | 
 |     { | 
 |       frame->pc = read_pc (); | 
 |  | 
 |       /* Part of the signal stuff...  See below.  */ | 
 |       if (stopped_by_random_signal) | 
 |         { | 
 |           /* We know we're in a system call mini-frame; was it | 
 |              NullApi or something else?  */ | 
 |           ra = SAVED_PC_AFTER_CALL (frame); | 
 |           if (ra >= null_start && ra < null_end) | 
 | 	    deprecated_set_frame_type (frame, SIGTRAMP_FRAME); | 
 |           /* There might also be an indirect call to the mini-frame, | 
 |              putting one more return address on the stack.  (XP only, | 
 |              I think?)  This can't (reasonably) return the address of the  | 
 |              signal handler caller unless it's that situation, so this | 
 |              is safe.  */ | 
 |           ra = read_memory_unsigned_integer (read_register (SP_REGNUM) + 4, 4); | 
 |           if (ra >= null_start && ra < null_end) | 
 | 	    deprecated_set_frame_type (frame, SIGTRAMP_FRAME); | 
 |         } | 
 |       return; | 
 |     } | 
 |  | 
 |   if (!(get_frame_type (frame->next) == SIGTRAMP_FRAME)) | 
 |     { | 
 |       frame->pc = read_memory_integer (frame->next->frame + 4, 4); | 
 |       return; | 
 |     } | 
 |  | 
 |   /* This is messy (actually AWFUL)...  The "trampoline" might be 2, 3  | 
 |      or all 5 entities on the frame.  | 
 |  | 
 |      Chunk 1 will be present when we're actually in a signal handler. | 
 |      Chunk 2 will be present when an asynchronous signal (one that | 
 |      didn't come in with a system call) is present. | 
 |      We may not (yet) be in the handler, if we're just returning | 
 |      from the call. | 
 |      When we're actually in a handler taken from an asynchronous | 
 |      signal, both will be present. | 
 |  | 
 |      Chunk 1: | 
 |      PdxSignalDeliverer's frame  | 
 |      + Context struct    -- not accounted for in any frame | 
 |  | 
 |      Chunk 2: | 
 |      + PdxNullPosixApi's frame  | 
 |      + PdxNullApiCaller's frame | 
 |      + Context struct = 0x230  not accounted for in any frame | 
 |  | 
 |      The symbol names come from examining objdumps of psxdll.dll; | 
 |      they don't appear in the runtime image. | 
 |  | 
 |      For gdb's purposes, we can pile all this into one frame.  */ | 
 |  | 
 |   ra = frame->next->pc; | 
 |   /* Are we already pointing at PdxNullPosixApi?  We are if | 
 |      this is a signal frame, we're at next-to-top, and were stopped | 
 |      by a random signal (if it wasn't the right address under | 
 |      these circumstances, we wouldn't be here at all by tests above | 
 |      on the prior frame).  */ | 
 |   if (frame->next->next == NULL && stopped_by_random_signal) | 
 |     { | 
 |       /* We're pointing at the frame FOR PdxNullApi.  */ | 
 |       fm = frame->frame; | 
 |     } | 
 |   else | 
 |     { | 
 |       /* No...  We must be pointing at the frame that was called | 
 |          by PdxSignalDeliverer; back up across the whole mess.  */ | 
 |  | 
 |       /* Extract the frame for PdxSignalDeliverer.  Note: | 
 |          DEPRECATED_FRAME_CHAIN used the "old" frame pointer because | 
 |          we were a deliverer.  Get the address of the context record | 
 |          that's on here frameless.  */ | 
 |       context = read_memory_integer (frame->frame, 4);  /* an Arg */ | 
 |  | 
 |       /* Now extract the frame pointer contained in the context.  */ | 
 |       fm = read_memory_integer (context + mcontext_EBP_greg_offset, 4); | 
 |  | 
 |       ra = read_memory_integer (context + mcontext_EIP_greg_offset, 4); | 
 |  | 
 |       /* We need to know if we're in a system call because we'll be | 
 |          in a syscall mini-frame, if so, and the rules are different.  */ | 
 |       t = (long) read_memory_integer (context + mcontext_syscall_greg_offset, | 
 |                                       4); | 
 |       /* t contains 0 if running free, 1 if blocked on a system call, | 
 |          and 2 if blocked on an exception message (e.g. a trap); | 
 |          we don't expect to get here with a 2.  */ | 
 |       if (t != 1) | 
 |         { | 
 |           /* Not at a system call, therefore it can't be NullApi.  */ | 
 |           frame->pc = ra; | 
 |           frame->frame = fm; | 
 |           return; | 
 |         } | 
 |  | 
 |       /* It's a system call...  Mini frame, then look for NullApi.  */ | 
 |       /* Get the RA (on the stack) associated with this...  It's | 
 |          a system call mini-frame.  */ | 
 |       ra = read_memory_integer (context + mcontext_UESP_greg_offset, 4); | 
 |  | 
 |       if (winver >= 51) | 
 |         { | 
 |           /* Newer versions of Windows NT interpose another return | 
 |              address (but no other "stack frame" stuff) that we need | 
 |              to simply ignore here.  */ | 
 |           ra += 4; | 
 |         } | 
 |  | 
 |       ra = read_memory_integer (ra, 4); | 
 |  | 
 |       if (!(ra >= null_start && ra < null_end)) | 
 |         { | 
 |           /* No Null API present; we're done.  */ | 
 |           frame->pc = ra; | 
 |           frame->frame = fm; | 
 |           return; | 
 |         } | 
 |     } | 
 |  | 
 |   /* At this point, we're looking at the frame for PdxNullPosixApi, | 
 |      in either case. | 
 |  | 
 |      PdxNullPosixApi is called by PdxNullApiCaller (which in turn | 
 |      is called by _PdxNullApiCaller (note the _).) | 
 |      PdxNullPosixApiCaller (no _) is a frameless function. | 
 |  | 
 |      The saved frame pointer is as fm, but it's not of interest | 
 |      to us because it skips us over the saved context, which is | 
 |      the wrong thing to do, because it skips the interrrupted | 
 |      routine!  PdxNullApiCaller takes as its only argument the | 
 |      address of the context of the interrupded function (which | 
 |      is really in no frame, but jammed on the stack by the system) | 
 |  | 
 |      So: fm+0: saved bp | 
 |      fm+4: return address to _PdxNullApiCaller | 
 |      fm+8: arg to PdxNullApiCaller pushed by _Pdx...  */ | 
 |  | 
 |   fm = read_memory_integer (fm + 0x8, 4); | 
 |  | 
 |   /* Extract the second context record.  */ | 
 |  | 
 |   ra = read_memory_integer (fm + mcontext_EIP_greg_offset, 4); | 
 |   fm = read_memory_integer (fm + mcontext_EBP_greg_offset, 4); | 
 |  | 
 |   frame->frame = fm; | 
 |   frame->pc = ra; | 
 |  | 
 |   return; | 
 | } | 
 |  | 
 | static CORE_ADDR | 
 | i386_interix_frame_saved_pc (struct frame_info *fi) | 
 | { | 
 |   /* Assume that we've already unwound enough to have the caller's address | 
 |      if we're dealing with a signal handler caller (And if that fails, | 
 |      return 0).  */ | 
 |   if ((get_frame_type (fi) == SIGTRAMP_FRAME)) | 
 |     return fi->next ? fi->next->pc : 0; | 
 |   else | 
 |     return read_memory_integer (fi->frame + 4, 4); | 
 | } | 
 |  | 
 | static void | 
 | i386_interix_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) | 
 | { | 
 |   struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); | 
 |  | 
 |   tdep->struct_return = reg_struct_return; | 
 |   tdep->jb_pc_offset = jump_buffer_Eip_offset; | 
 |  | 
 |   set_gdbarch_decr_pc_after_break (gdbarch, 0); | 
 |   set_gdbarch_pc_in_sigtramp (gdbarch, i386_interix_pc_in_sigtramp); | 
 |   set_gdbarch_in_solib_call_trampoline (gdbarch, | 
 |                                         i386_interix_in_solib_call_trampoline); | 
 |   set_gdbarch_skip_trampoline_code (gdbarch, | 
 |                                     i386_interix_skip_trampoline_code); | 
 |   set_gdbarch_deprecated_init_extra_frame_info (gdbarch, i386_interix_back_one_frame); | 
 |   set_gdbarch_deprecated_init_frame_pc (gdbarch, init_frame_pc_noop); | 
 |   set_gdbarch_deprecated_frame_chain_valid (gdbarch, i386_interix_frame_chain_valid); | 
 |   set_gdbarch_deprecated_frame_saved_pc (gdbarch, i386_interix_frame_saved_pc); | 
 |   set_gdbarch_name_of_malloc (gdbarch, "_malloc"); | 
 | } | 
 |  | 
 | static enum gdb_osabi | 
 | i386_interix_osabi_sniffer (bfd * abfd) | 
 | { | 
 |   char *target_name = bfd_get_target (abfd); | 
 |  | 
 |   if (strcmp (target_name, "pei-i386") == 0) | 
 |     return GDB_OSABI_INTERIX; | 
 |  | 
 |   return GDB_OSABI_UNKNOWN; | 
 | } | 
 |  | 
 | void | 
 | _initialize_i386_interix_tdep (void) | 
 | { | 
 |   gdbarch_register_osabi_sniffer (bfd_arch_i386, bfd_target_coff_flavour, | 
 |                                   i386_interix_osabi_sniffer); | 
 |  | 
 |   gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_INTERIX, | 
 |                           i386_interix_init_abi); | 
 | } |