| /* 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: 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_frame_chain_valid (gdbarch, i386_interix_frame_chain_valid); |
| set_gdbarch_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); |
| } |