summaryrefslogtreecommitdiff
path: root/gdb/i386-interix-tdep.c
blob: e25242c325e73d18fc75fa69efa598fee733708a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
/* 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_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);
}