/* 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 "arch-utils.h" #include "frame.h" #include "gdb_string.h" #include "gdbcore.h" #include "gdbtypes.h" #include "inferior.h" #include "libbfd.h" #include "osabi.h" /* FIXME: This is not right, we should not be including this file in -tdep module. But this is needed by interix_back_one_frame. */ #include /* ??? Why do we need an extern here??? */ extern CORE_ADDR skip_trampoline_code (CORE_ADDR pc, char *name); /* Some constants describing the i386 interix target */ static const int i386_interix_long_bit = 32; static const int i386_interix_long_long_bit = 64; static const int i386_interix_ptr_bit = 32; static const CORE_ADDR i386_interix_decr_pc_after_break = 0; /* 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 */ 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 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 skip_trampoline_code (pc, name); } static CORE_ADDR i386_interix_skip_trampoline_code (CORE_ADDR pc) { return skip_trampoline_code (pc, 0); } static void i386_interix_init_frame_pc (int fromleaf, struct frame_info *prev) { /* Nothing to do on Interix */ } 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 thisframe->signal_handler_caller || (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 signal_handler_caller; this replaces a boatload of nested macros, as well. */ static void i386_interix_back_one_frame (fromleaf, frame) int fromleaf; struct frame_info *frame; { CORE_ADDR ra; CORE_ADDR fm; CORE_ADDR context; long t; if (frame == NULL) abort(); 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) frame->signal_handler_caller = 1; /* 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 ( (CORE_ADDR) read_register (SP_REGNUM)+4, 4); if (ra >= null_start && ra < null_end) frame->signal_handler_caller = 1; } return; } if (!frame->next->signal_handler_caller) { frame->pc = (CORE_ADDR)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...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 = (CORE_ADDR)read_memory_integer (context + offsetof(mcontext_t, gregs.gregs[EBP]), 4); ra = (CORE_ADDR)read_memory_integer (context + offsetof(mcontext_t, gregs.gregs[EIP]), 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; reserved[1] 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. */ t = (long)read_memory_integer (context + offsetof(mcontext_t, gregs.reserved[1]), 4); 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 = (CORE_ADDR)read_memory_integer (context + offsetof(mcontext_t, gregs.gregs[UESP]), 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 = (CORE_ADDR)read_memory_integer(ra,4); /* We might be done (no Null API if so) */ 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 = (CORE_ADDR)read_memory_integer (fm+0x8, 4); /* Extract the second context record */ ra = (CORE_ADDR)read_memory_integer (fm + offsetof(mcontext_t, gregs.gregs[EIP]), 4); fm = (CORE_ADDR)read_memory_integer (fm + offsetof(mcontext_t, gregs.gregs[EBP]), 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 (fi->signal_handler_caller) return fi->next ? fi->next->pc : 0; else return (CORE_ADDR) read_memory_integer (fi->frame + 4, 4); } static int i386_interix_use_struct_convention (int gcc_p, struct type *type) { /* Directly copied from nbsd where this function is no longer defined, as a generic mechanism is used. Maybe we should take advantage of this mechanism too? */ return !(TYPE_LENGTH (type) == 1 || TYPE_LENGTH (type) == 2 || TYPE_LENGTH (type) == 4 || TYPE_LENGTH (type) == 8); } void i386_interix_cache_trampoline_addresses_from_bfd (void) { /* This is painful, but there doesn't seem to be a better way. See interix tm.h, IN_SIGTRAMP, and procfs.c for more details. */ asection *section; pstatus_t *p; section = bfd_get_section_by_name (core_bfd, ".pstatus"); if (section == NULL) { /* This should never happen, we are just being extra cautious. */ warning (".pstatus section not found."); return; } p = (pstatus_t *)section->contents; if (p == NULL) { p = (pstatus_t *)bfd_alloc(core_bfd, section->_raw_size); bfd_get_section_contents (core_bfd, section, p, 0, section->_raw_size); } tramp_start = (CORE_ADDR)p->pr_signaldeliverer; tramp_end = (CORE_ADDR)p->pr_endsignaldeliverer; null_start = (CORE_ADDR)p->pr_nullapi; null_end = (CORE_ADDR)p->pr_endnullapi; } void i386_interix_cache_trampoline_addresses (CORE_ADDR trampoline_start, CORE_ADDR trampoline_end, CORE_ADDR nullapi_start, CORE_ADDR nullapi_end) { /* On Interix, the location of the trampoline code can float, and it's not part of the symbol table (because it's internal to a DLL). However, the system knows where it is, and tells us, so we capture that info here. */ /* While we're doing such ugly things, find out which version of NT this is, so we can change our unwind rules a bit. FIXME: If we move this, move the include of utsname.h, too.*/ tramp_start = trampoline_start; tramp_end = trampoline_end; null_start = nullapi_start; null_end = nullapi_end; } static void i386_interix_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) { /* ??? Externs, temporarily defined */ extern int get_longjmp_target (CORE_ADDR *pc); struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); set_gdbarch_long_bit (gdbarch, i386_interix_long_bit); set_gdbarch_long_long_bit (gdbarch, i386_interix_long_long_bit); set_gdbarch_use_struct_convention (gdbarch, i386_interix_use_struct_convention); set_gdbarch_ptr_bit (gdbarch, i386_interix_ptr_bit); set_gdbarch_decr_pc_after_break (gdbarch, i386_interix_decr_pc_after_break); set_gdbarch_pc_in_sigtramp (gdbarch, i386_interix_pc_in_sigtramp); /* ??? The following may already be the default on i386 architecture */ 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_init_extra_frame_info (gdbarch, i386_interix_back_one_frame); set_gdbarch_init_frame_pc (gdbarch, i386_interix_init_frame_pc); set_gdbarch_frame_chain_valid (gdbarch, i386_interix_frame_chain_valid); set_gdbarch_frame_saved_pc (gdbarch, i386_interix_frame_saved_pc); /* ??? the following may already be the default for i386 architecture */ set_gdbarch_get_longjmp_target (gdbarch, get_longjmp_target); } 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, GDB_OSABI_INTERIX, i386_interix_init_abi); }