This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[RFA/commit+doco 2/2] Windows x64 SEH unwinder.


Hello,

This is the main part of the patch series, which actually
adds the unwinder.

One element worth mentioning, perhaps, is the fact that we prepend
the unwinder, and the sniffer is the default_frame_sniffer which
always returns 1 (absence of SEH info is normal and means it is
a leaf function).  This effectively means that all other unwinders
effectively get shunted. And in particular, I do not think that
the DWARF unwinder will kick in even if DWARF unwind info is found.

The problem is that we want to be ahead of the default amd64-tdep
unwinders, which is kind of a last-resort unwinder doing a good
job under limited situations only. We'd like to be behind the DWARF
unwinder if we could, but I don't think there is a way to ensure
that yet.

In practice, this shouldn't be a problem, since this unwinder
has been very reliable for us so far.  But it does assume that
the compiler is recent enough to generate native SEH data which,
for GCC, means GCC 4.7 (I have been told). On the other hand,
it looks like the first GCC release to support x64-windows was
GCC 4.6.

I don't really see a real way of supporting both old and new versions
of GCC, unless we have a way of more finely ordering the unwinders.
Worse case scenario, we stop supporting code generated by GCC 4.6.
Or an alternative option is to provide a setting to disable this
unwinder.

gdb/ChangeLog (from Tristan Gingold and Joel Brobecker):

        * amd64-windows-tdep.c: #include "objfiles.h", "frame-unwind.h",
        "coff/internal.h", "coff/i386.h", "coff/pe.h" and "libcoff.h".
        (struct amd64_windows_frame_cache): New struct.
        (amd64_windows_w2gdb_regnum): New global.
        (amd64_windows_frame_decode_epilogue)
        (amd64_windows_frame_decode_insns, amd64_windows_find_unwind_info)
        (amd64_windows_frame_cache, amd64_windows_frame_prev_register)
        (amd64_windows_frame_this_id): New functions.
        (amd64_windows_frame_unwind): New static global.
        (amd64_windows_skip_prologue): New function.
        (amd64_windows_init_abi): Call frame_unwind_prepend_unwinder
        with amd64_windows_frame_unwind. Call set_gdbarch_skip_prologue
        with amd64_windows_skip_prologue.

        * NEWS: Add entry mentioning support for native Windows x64
        SEH data.

Tested on x86_64-windows. I will wait until the end of the week
for comments and suggestions...

Thank you :)
-- 
Joel

---
 gdb/NEWS                 |    2 +
 gdb/amd64-windows-tdep.c |  688 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 690 insertions(+)

diff --git a/gdb/NEWS b/gdb/NEWS
index 3451505..d981ac5 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -134,6 +134,8 @@ show print type typedefs
   feature to be enabled.  For more information, see:
       http://fedoraproject.org/wiki/Features/MiniDebugInfo
 
+* GDB can now use Windows x64 unwinding data.
+
 *** Changes in GDB 7.5
 
 * GDB now supports x32 ABI.  Visit <http://sites.google.com/site/x32abi/>
diff --git a/gdb/amd64-windows-tdep.c b/gdb/amd64-windows-tdep.c
index e7addfc..90f7628 100644
--- a/gdb/amd64-windows-tdep.c
+++ b/gdb/amd64-windows-tdep.c
@@ -25,6 +25,12 @@
 #include "regcache.h"
 #include "windows-tdep.h"
 #include "frame.h"
+#include "objfiles.h"
+#include "frame-unwind.h"
+#include "coff/internal.h"
+#include "coff/i386.h"
+#include "coff/pe.h"
+#include "libcoff.h"
 
 /* The registers used to pass integer arguments during a function call.  */
 static int amd64_windows_dummy_call_integer_regs[] =
@@ -198,6 +204,685 @@ amd64_windows_auto_wide_charset (void)
   return "UTF-16";
 }
 
+struct amd64_windows_frame_cache
+{
+  /* ImageBase for the module.  */
+  CORE_ADDR image_base;
+
+  /* Function start rva.  */
+  CORE_ADDR start_rva;
+
+  /* Next instruction to be executed.  */
+  CORE_ADDR pc;
+
+  /* Current sp.  */
+  CORE_ADDR sp;
+
+  /* Address of saved integer and xmm registers.  */
+  CORE_ADDR prev_reg_addr[16];
+  CORE_ADDR prev_xmm_addr[16];
+
+  /* These two next fields are set only for machine info frames.  */
+
+  /* Likewise for RIP.  */
+  CORE_ADDR prev_rip_addr;
+
+  /* Likewise for RSP.  */
+  CORE_ADDR prev_rsp_addr;
+
+  /* Address of the previous frame.  */
+  CORE_ADDR prev_sp;
+};
+
+/* Convert a Windows register number to gdb.  */
+static const enum amd64_regnum amd64_windows_w2gdb_regnum[] =
+{
+  AMD64_RAX_REGNUM,
+  AMD64_RCX_REGNUM,
+  AMD64_RDX_REGNUM,
+  AMD64_RBX_REGNUM,
+  AMD64_RSP_REGNUM,
+  AMD64_RBP_REGNUM,
+  AMD64_RSI_REGNUM,
+  AMD64_RDI_REGNUM,
+  AMD64_R8_REGNUM,
+  AMD64_R9_REGNUM,
+  AMD64_R10_REGNUM,
+  AMD64_R11_REGNUM,
+  AMD64_R12_REGNUM,
+  AMD64_R13_REGNUM,
+  AMD64_R14_REGNUM,
+  AMD64_R15_REGNUM
+};
+
+/* Try to recognize and decode an epilogue sequence.
+
+   Return -1 if we fail to read the instructions for any reason.
+   Return 1 if an epilogue sequence was recognized, 0 otherwise.  */
+
+static int
+amd64_windows_frame_decode_epilogue (struct frame_info *this_frame,
+				     struct amd64_windows_frame_cache *cache)
+{
+  /* Not in a prologue, so maybe in an epilogue.  For the rules, cf
+     http://msdn.microsoft.com/en-us/library/tawsa7cb.aspx
+     Furthermore, according to RtlVirtualUnwind, the complete list of
+     epilog marker is:
+     - ret                      [c3]
+     - ret n                    [c2 imm16]
+     - rep ret                  [f3 c3]
+     - jmp imm8 | imm32         [eb rel8] or [e9 rel32]
+     - jmp qword ptr imm32                 - not handled
+     - rex jmp reg              [4X ff eY]
+     I would add:
+     -  pop reg                 [41 58-5f] or [58-5f]
+
+     We don't care about the instruction deallocating the frame:
+     if it hasn't been executed, we can safely decode the insns;
+     if it has been executed, the following epilog decoding will
+     work.  */
+  CORE_ADDR pc = cache->pc;
+  CORE_ADDR cur_sp = cache->sp;
+  struct gdbarch *gdbarch = get_frame_arch (this_frame);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+
+  while (1)
+    {
+      gdb_byte op;
+      gdb_byte rex;
+
+      if (target_read_memory (pc, &op, 1) != 0)
+	return -1;
+
+      if (op == 0xc3)
+	{
+	  /* Ret.  */
+	  cache->prev_rip_addr = cur_sp;
+	  cache->prev_sp = cur_sp + 8;
+	  return 1;
+	}
+      else if (op == 0xeb)
+	{
+	  /* jmp rel8  */
+	  gdb_byte rel8;
+
+	  if (target_read_memory (pc + 1, &rel8, 1) != 0)
+	    return -1;
+	  pc = pc + 2 + (signed char) rel8;
+	}
+      else if (op == 0xec)
+	{
+	  /* jmp rel32  */
+	  gdb_byte rel32[4];
+
+	  if (target_read_memory (pc + 1, rel32, 4) != 0)
+	    return -1;
+	  pc = pc + 5 + extract_signed_integer (rel32, 4, byte_order);
+	}
+      else if (op >= 0x58 && op <= 0x5f)
+	{
+	  /* pop reg  */
+	  cache->prev_reg_addr[amd64_windows_w2gdb_regnum[op & 0x0f]] = cur_sp;
+	  cur_sp += 8;
+	}
+      else if (op == 0xc2)
+	{
+	  /* ret n  */
+	  gdb_byte imm16[2];
+
+	  if (target_read_memory (pc + 1, imm16, 2) != 0)
+	    return -1;
+	  cache->prev_rip_addr = cur_sp;
+	  cache->prev_sp = cur_sp
+	    + extract_unsigned_integer (imm16, 4, byte_order);
+	  return 1;
+	}
+      else if (op == 0xf3)
+	{
+	  /* rep; ret  */
+	  gdb_byte op1;
+
+	  if (target_read_memory (pc + 2, &op1, 1) != 0)
+	    return -1;
+	  if (op1 != 0xc3)
+	    return 0;
+
+	  cache->prev_rip_addr = cur_sp;
+	  cache->prev_sp = cur_sp + 8;
+	  return 1;
+	}
+      else if (op < 0x40 || op > 0x4f)
+	{
+	  /* Not REX, so unknown.  */
+	  return 0;
+	}
+
+      /* Got a REX prefix, read next byte.  */
+      rex = op;
+      if (target_read_memory (pc + 1, &op, 1) != 0)
+	return -1;
+
+      if (op >= 0x58 && op <= 0x5f)
+	{
+	  /* pop reg  */
+	  unsigned int reg;
+
+	  reg = (op & 0x0f) | ((rex & 1) << 3);
+	  cache->prev_reg_addr[amd64_windows_w2gdb_regnum[reg]] = cur_sp;
+	  cur_sp += 8;
+	}
+      else if (op == 0xff)
+	{
+	  /* rex jmp reg  */
+	  gdb_byte op1;
+	  unsigned int reg;
+	  gdb_byte buf[8];
+
+	  if (target_read_memory (pc + 2, &op1, 1) != 0)
+	    return -1;
+	  if ((op1 & 0xf8) != 0xe0)
+	    return 0;
+	  reg = (op1 & 0x0f) | ((rex & 1) << 3);
+
+	  get_frame_register (this_frame,
+			      amd64_windows_w2gdb_regnum[reg], buf);
+	  pc = extract_unsigned_integer (buf, 8, byte_order);
+	}
+      else
+	return 0;
+
+      /* Allow the user to break this loop.  */
+      QUIT;
+    }
+}
+
+/* Decode and execute unwind insns at UNWIND_INFO.  */
+
+static void
+amd64_windows_frame_decode_insns (struct frame_info *this_frame,
+				  struct amd64_windows_frame_cache *cache,
+				  CORE_ADDR unwind_info)
+{
+  CORE_ADDR save_addr = 0;
+  CORE_ADDR cur_sp = cache->sp;
+  struct gdbarch *gdbarch = get_frame_arch (this_frame);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  int j;
+
+  for (j = 0; ; j++)
+    {
+      struct external_pex64_unwind_info ex_ui;
+      /* There are at most 256 16-bit unwind insns.  */
+      gdb_byte insns[2 * 256];
+      gdb_byte *p;
+      gdb_byte *end_insns;
+      unsigned char codes_count;
+      unsigned char frame_reg;
+      unsigned char frame_off;
+
+      /* Read and decode header.  */
+      if (target_read_memory (cache->image_base + unwind_info,
+			      (gdb_byte *) &ex_ui, sizeof (ex_ui)) != 0)
+	return;
+
+      if (unwind_debug)
+	fprintf_unfiltered
+	  (gdb_stdlog,
+	   "amd64_windows_frame_play_insn: "
+	   "%s: ver: %02x, plgsz: %02x, cnt: %02x, frame: %02x\n",
+	   paddress (gdbarch, unwind_info),
+	   ex_ui.Version_Flags, ex_ui.SizeOfPrologue,
+	   ex_ui.CountOfCodes, ex_ui.FrameRegisterOffset);
+
+      /* Check version.  */
+      if (PEX64_UWI_VERSION (ex_ui.Version_Flags) != 1)
+	return;
+
+      if (j == 0
+	  && (cache->pc >=
+	      cache->image_base + cache->start_rva + ex_ui.SizeOfPrologue))
+	{
+	  /* Not in the prologue; try to decode an epilog.  */
+	  if (amd64_windows_frame_decode_epilogue (this_frame, cache) == 1)
+	    return;
+
+	  /* Not in an epilog.  Clear possible side effects.  */
+	  memset (cache->prev_reg_addr, 0, sizeof (cache->prev_reg_addr));
+	}
+
+      codes_count = ex_ui.CountOfCodes;
+      frame_reg = PEX64_UWI_FRAMEREG (ex_ui.FrameRegisterOffset);
+
+      if (frame_reg != 0)
+	{
+	  /* According to msdn:
+	     If an FP reg is used, then any unwind code taking an offset must
+	     only be used after the FP reg is established in the prolog.  */
+	  gdb_byte buf[8];
+	  int frreg = amd64_windows_w2gdb_regnum[frame_reg];
+
+	  get_frame_register (this_frame, frreg, buf);
+	  save_addr = extract_unsigned_integer (buf, 8, byte_order);
+
+	  if (unwind_debug)
+	    fprintf_unfiltered (gdb_stdlog, "   frame_reg=%s, val=%s\n",
+				gdbarch_register_name (gdbarch, frreg),
+				paddress (gdbarch, save_addr));
+	}
+
+      /* Read opcodes.  */
+      if (codes_count != 0
+	  && target_read_memory (cache->image_base + unwind_info
+				 + sizeof (ex_ui),
+				 insns, codes_count * 2) != 0)
+	return;
+
+      end_insns = &insns[codes_count * 2];
+      for (p = insns; p < end_insns; p += 2)
+	{
+	  int reg;
+
+	  if (unwind_debug)
+	    fprintf_unfiltered
+	      (gdb_stdlog, "   op #%u: off=0x%02x, insn=0x%02x\n",
+	       (unsigned) (p - insns), p[0], p[1]);
+
+	  /* Virtually execute the operation.  */
+	  if (cache->pc >= cache->image_base + cache->start_rva + p[0])
+	    {
+	      /* If there is no frame registers defined, the current value of
+		 rsp is used instead.  */
+	      if (frame_reg == 0)
+		save_addr = cur_sp;
+
+	      switch (PEX64_UNWCODE_CODE (p[1]))
+		{
+		case UWOP_PUSH_NONVOL:
+		  /* Push pre-decrements RSP.  */
+		  reg = amd64_windows_w2gdb_regnum[PEX64_UNWCODE_INFO (p[1])];
+		  cache->prev_reg_addr[reg] = cur_sp;
+		  cur_sp += 8;
+		  break;
+		case UWOP_ALLOC_LARGE:
+		  if (PEX64_UNWCODE_INFO (p[1]) == 0)
+		    cur_sp +=
+		      8 * extract_unsigned_integer (p + 2, 2, byte_order);
+		  else if (PEX64_UNWCODE_INFO (p[1]) == 1)
+		    cur_sp += extract_unsigned_integer (p + 2, 4, byte_order);
+		  else
+		    return;
+		  break;
+		case UWOP_ALLOC_SMALL:
+		  cur_sp += 8 + 8 * PEX64_UNWCODE_INFO (p[1]);
+		  break;
+		case UWOP_SET_FPREG:
+		  cur_sp = save_addr
+		    - PEX64_UWI_FRAMEOFF (ex_ui.FrameRegisterOffset) * 16;
+		  break;
+		case UWOP_SAVE_NONVOL:
+		  reg = amd64_windows_w2gdb_regnum[PEX64_UNWCODE_INFO (p[1])];
+		  cache->prev_reg_addr[reg] = save_addr
+		    - 8 * extract_unsigned_integer (p + 2, 2, byte_order);
+		  break;
+		case UWOP_SAVE_NONVOL_FAR:
+		  reg = amd64_windows_w2gdb_regnum[PEX64_UNWCODE_INFO (p[1])];
+		  cache->prev_reg_addr[reg] = save_addr
+		    - 8 * extract_unsigned_integer (p + 2, 4, byte_order);
+		  break;
+		case UWOP_SAVE_XMM128:
+		  cache->prev_xmm_addr[PEX64_UNWCODE_INFO (p[1])] =
+		    save_addr
+		    - 16 * extract_unsigned_integer (p + 2, 2, byte_order);
+		  break;
+		case UWOP_SAVE_XMM128_FAR:
+		  cache->prev_xmm_addr[PEX64_UNWCODE_INFO (p[1])] =
+		    save_addr
+		    - 16 * extract_unsigned_integer (p + 2, 4, byte_order);
+		  break;
+		case UWOP_PUSH_MACHFRAME:
+		  if (PEX64_UNWCODE_INFO (p[1]) == 0)
+		    {
+		      cache->prev_rip_addr = cur_sp + 0;
+		      cache->prev_rsp_addr = cur_sp + 24;
+		      cur_sp += 40;
+		    }
+		  else if (PEX64_UNWCODE_INFO (p[1]) == 1)
+		    {
+		      cache->prev_rip_addr = cur_sp + 8;
+		      cache->prev_rsp_addr = cur_sp + 32;
+		      cur_sp += 48;
+		    }
+		  else
+		    return;
+		  break;
+		default:
+		  return;
+		}
+	    }
+
+	  /* Adjust with the length of the opcode.  */
+	  switch (PEX64_UNWCODE_CODE (p[1]))
+	    {
+	    case UWOP_PUSH_NONVOL:
+	    case UWOP_ALLOC_SMALL:
+	    case UWOP_SET_FPREG:
+	    case UWOP_PUSH_MACHFRAME:
+	      break;
+	    case UWOP_ALLOC_LARGE:
+	      if (PEX64_UNWCODE_INFO (p[1]) == 0)
+		p += 2;
+	      else if (PEX64_UNWCODE_INFO (p[1]) == 1)
+		p += 4;
+	      else
+		return;
+	      break;
+	    case UWOP_SAVE_NONVOL:
+	    case UWOP_SAVE_XMM128:
+	      p += 2;
+	      break;
+	    case UWOP_SAVE_NONVOL_FAR:
+	    case UWOP_SAVE_XMM128_FAR:
+	      p += 4;
+	      break;
+	    default:
+	      return;
+	    }
+	}
+      if (PEX64_UWI_FLAGS (ex_ui.Version_Flags) != UNW_FLAG_CHAININFO)
+	break;
+      else
+	{
+	  /* Read the chained unwind info.  */
+	  struct external_pex64_runtime_function d;
+	  CORE_ADDR chain_vma;
+
+	  chain_vma = cache->image_base + unwind_info
+	    + sizeof (ex_ui) + ((codes_count + 1) & ~1) * 2 + 8;
+
+	  if (target_read_memory (chain_vma, (gdb_byte *) &d, sizeof (d)) != 0)
+	    return;
+
+	  cache->start_rva =
+	    extract_unsigned_integer (d.rva_BeginAddress, 4, byte_order);
+	  unwind_info =
+	    extract_unsigned_integer (d.rva_EndAddress, 4, byte_order);
+	}
+
+      /* Allow the user to break this loop.  */
+      QUIT;
+    }
+  /* PC is saved by the call.  */
+  if (cache->prev_rip_addr == 0)
+    cache->prev_rip_addr = cur_sp;
+  cache->prev_sp = cur_sp + 8;
+
+  if (unwind_debug)
+    fprintf_unfiltered (gdb_stdlog, "   prev_sp: %s, prev_pc @%s\n",
+			paddress (gdbarch, cache->prev_sp),
+			paddress (gdbarch, cache->prev_rip_addr));
+}
+
+/* Find SEH unwind info for PC, returning 0 on success.
+
+   UNWIND_INFO is set to the rva of unwind info address, IMAGE_BASE
+   to the base address of the corresponding image, and START_RVA
+   to the rva of the function containing PC.  */
+
+static int
+amd64_windows_find_unwind_info (struct gdbarch *gdbarch, CORE_ADDR pc,
+				CORE_ADDR *unwind_info,
+				CORE_ADDR *image_base,
+				CORE_ADDR *start_rva)
+{
+  struct obj_section *sec;
+  pe_data_type *pe;
+  IMAGE_DATA_DIRECTORY *dir;
+  struct objfile *objfile;
+  unsigned long lo, hi;
+  CORE_ADDR base;
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+
+  /* Get the corresponding exception directory.  */
+  sec = find_pc_section (pc);
+  if (sec == NULL)
+    return -1;
+  objfile = sec->objfile;
+  pe = pe_data (sec->objfile->obfd);
+  dir = &pe->pe_opthdr.DataDirectory[PE_EXCEPTION_TABLE];
+
+  base = pe->pe_opthdr.ImageBase
+    + ANOFFSET (objfile->section_offsets, SECT_OFF_TEXT (objfile));
+  *image_base = base;
+
+  /* Find the entry.
+
+     Note: This does not handle dynamically added entries (for JIT
+     engines).  For this, we would need to ask the kernel directly,
+     which means getting some info from the native layer.  For the
+     rest of the code, however, it's probably faster to search
+     the entry ourselves.  */
+  lo = 0;
+  hi = dir->Size / sizeof (struct external_pex64_runtime_function);
+  *unwind_info = 0;
+  while (lo <= hi)
+    {
+      unsigned long mid = lo + (hi - lo) / 2;
+      struct external_pex64_runtime_function d;
+      CORE_ADDR sa, ea;
+
+      if (target_read_memory (base + dir->VirtualAddress + mid * sizeof (d),
+			      (gdb_byte *) &d, sizeof (d)) != 0)
+	return -1;
+
+      sa = extract_unsigned_integer (d.rva_BeginAddress, 4, byte_order);
+      ea = extract_unsigned_integer (d.rva_EndAddress, 4, byte_order);
+      if (pc < base + sa)
+	hi = mid - 1;
+      else if (pc >= base + ea)
+	lo = mid + 1;
+      else if (pc >= base + sa && pc < base + ea)
+	{
+	  /* Got it.  */
+	  *start_rva = sa;
+	  *unwind_info =
+	    extract_unsigned_integer (d.rva_UnwindData, 4, byte_order);
+	  break;
+	}
+      else
+	break;
+    }
+
+  if (unwind_debug)
+    fprintf_unfiltered
+      (gdb_stdlog,
+       "amd64_windows_find_unwind_data:  image_base=%s, unwind_data=%s\n",
+       paddress (gdbarch, base), paddress (gdbarch, *unwind_info));
+
+  if (*unwind_info & 1)
+    {
+      /* Unofficially documented unwind info redirection, when UNWIND_INFO
+	 address is odd.  */
+      struct external_pex64_runtime_function d;
+      CORE_ADDR sa, ea;
+
+      if (target_read_memory (base + (*unwind_info & ~1),
+			      (gdb_byte *) &d, sizeof (d)) != 0)
+	return -1;
+
+      *start_rva =
+	extract_unsigned_integer (d.rva_BeginAddress, 4, byte_order);
+      *unwind_info =
+	extract_unsigned_integer (d.rva_EndAddress, 4, byte_order);
+    }
+  return 0;
+}
+
+/* Fill THIS_CACHE using the native amd64-windows unwinding data
+   for THIS_FRAME.  */
+
+static struct amd64_windows_frame_cache *
+amd64_windows_frame_cache (struct frame_info *this_frame, void **this_cache)
+{
+  struct gdbarch *gdbarch = get_frame_arch (this_frame);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  struct amd64_windows_frame_cache *cache;
+  char buf[8];
+  struct obj_section *sec;
+  pe_data_type *pe;
+  IMAGE_DATA_DIRECTORY *dir;
+  CORE_ADDR image_base;
+  CORE_ADDR pc;
+  struct objfile *objfile;
+  unsigned long lo, hi;
+  CORE_ADDR unwind_info = 0;
+
+  if (*this_cache)
+    return *this_cache;
+
+  cache = FRAME_OBSTACK_ZALLOC (struct amd64_windows_frame_cache);
+  *this_cache = cache;
+
+  /* Get current PC and SP.  */
+  pc = get_frame_pc (this_frame);
+  get_frame_register (this_frame, AMD64_RSP_REGNUM, buf);
+  cache->sp = extract_unsigned_integer (buf, 8, byte_order);
+  cache->pc = pc;
+
+  if (amd64_windows_find_unwind_info (gdbarch, pc, &unwind_info,
+				      &cache->image_base, &cache->start_rva))
+    return cache;
+
+  if (unwind_info == 0)
+    {
+      /* Assume a leaf function.  */
+      cache->prev_sp = cache->sp + 8;
+      cache->prev_rip_addr = cache->sp;
+    }
+  else
+    {
+      /* Decode unwind insns to compute saved addresses.  */
+      amd64_windows_frame_decode_insns (this_frame, cache, unwind_info);
+    }
+  return cache;
+}
+
+/* Implement the "prev_register" method of struct frame_unwind
+   using the standard Windows x64 SEH info.  */
+
+static struct value *
+amd64_windows_frame_prev_register (struct frame_info *this_frame,
+				   void **this_cache, int regnum)
+{
+  struct gdbarch *gdbarch = get_frame_arch (this_frame);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  struct amd64_windows_frame_cache *cache =
+    amd64_windows_frame_cache (this_frame, this_cache);
+  struct value *val;
+  CORE_ADDR prev;
+
+  if (unwind_debug)
+    fprintf_unfiltered (gdb_stdlog,
+			"amd64_windows_frame_prev_register %s for sp=%s\n",
+			gdbarch_register_name (gdbarch, regnum),
+			paddress (gdbarch, cache->prev_sp));
+
+  if (regnum >= AMD64_XMM0_REGNUM && regnum <= AMD64_XMM0_REGNUM + 15)
+      prev = cache->prev_xmm_addr[regnum - AMD64_XMM0_REGNUM];
+  else if (regnum == AMD64_RSP_REGNUM)
+    {
+      prev = cache->prev_rsp_addr;
+      if (prev == 0)
+	return frame_unwind_got_constant (this_frame, regnum, cache->prev_sp);
+    }
+  else if (regnum >= AMD64_RAX_REGNUM && regnum <= AMD64_R15_REGNUM)
+    prev = cache->prev_reg_addr[regnum - AMD64_RAX_REGNUM];
+  else if (regnum == AMD64_RIP_REGNUM)
+    prev = cache->prev_rip_addr;
+  else
+    prev = 0;
+
+  if (prev && unwind_debug)
+    fprintf_unfiltered (gdb_stdlog, "  -> at %s\n", paddress (gdbarch, prev));
+
+  if (prev)
+    {
+      /* Register was saved.  */
+      return frame_unwind_got_memory (this_frame, regnum, prev);
+    }
+  else
+    {
+      /* Register is either volatile or not modified.  */
+      return frame_unwind_got_register (this_frame, regnum, regnum);
+    }
+}
+
+/* Implement the "this_id" method of struct frame_unwind using
+   the standard Windows x64 SEH info.  */
+
+static void
+amd64_windows_frame_this_id (struct frame_info *this_frame, void **this_cache,
+		   struct frame_id *this_id)
+{
+  struct gdbarch *gdbarch = get_frame_arch (this_frame);
+  struct amd64_windows_frame_cache *cache =
+    amd64_windows_frame_cache (this_frame, this_cache);
+
+  *this_id = frame_id_build (cache->prev_sp,
+			     cache->image_base + cache->start_rva);
+}
+
+/* Windows x64 SEH unwinder.  */
+
+static const struct frame_unwind amd64_windows_frame_unwind =
+{
+  NORMAL_FRAME,
+  default_frame_unwind_stop_reason,
+  &amd64_windows_frame_this_id,
+  &amd64_windows_frame_prev_register,
+  NULL,
+  default_frame_sniffer
+};
+
+/* Implement the "skip_prologue" gdbarch method.  */
+
+static CORE_ADDR
+amd64_windows_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
+{
+  CORE_ADDR func_addr;
+  CORE_ADDR unwind_info = 0;
+  CORE_ADDR image_base, start_rva;
+  struct external_pex64_unwind_info ex_ui;
+
+  /* Use prologue size from unwind info.  */
+  if (amd64_windows_find_unwind_info (gdbarch, pc, &unwind_info,
+				      &image_base, &start_rva) == 0)
+    {
+      if (unwind_info == 0)
+	{
+	  /* Leaf function.  */
+	  return pc;
+	}
+      else if (target_read_memory (image_base + unwind_info,
+				   (gdb_byte *) &ex_ui, sizeof (ex_ui)) == 0
+	       && PEX64_UWI_VERSION (ex_ui.Version_Flags) == 1)
+	return max (pc, image_base + start_rva + ex_ui.SizeOfPrologue);
+    }
+
+  /* See if we can determine the end of the prologue via the symbol
+     table.  If so, then return either the PC, or the PC after
+     the prologue, whichever is greater.  */
+  if (find_pc_partial_function (pc, NULL, &func_addr, NULL))
+    {
+      CORE_ADDR post_prologue_pc
+	= skip_prologue_using_sal (gdbarch, func_addr);
+
+      if (post_prologue_pc != 0)
+	return max (pc, post_prologue_pc);
+    }
+
+  return pc;
+}
+
 static void
 amd64_windows_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
 {
@@ -225,6 +910,9 @@ amd64_windows_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
 
   set_gdbarch_auto_wide_charset (gdbarch, amd64_windows_auto_wide_charset);
 
+  frame_unwind_prepend_unwinder (gdbarch, &amd64_windows_frame_unwind);
+  set_gdbarch_skip_prologue (gdbarch, amd64_windows_skip_prologue);
+
   set_solib_ops (gdbarch, &solib_target_so_ops);
 }
 
-- 
1.7.10.4


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]