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] Add Windows x64 SEH unwinder


Hi,

the Windows x64 ABI specifies unwind data info in order to unwind frames for propagating exceptions or getting back traces.
GCC emits this infos since version 4.7

This patch adds an unwinder that reads these data.  The main advantage is that gdb is now able to unwind through code compiled with other compilers (and through system libraries).

I haven't run the gdb testsuite on Windows x64 (I don't know if this is doable), but I have manually tested it on a few executables, including gdb itself.

Comments are welcome.

Tristan.

2012-06-15  Tristan Gingold  <gingold@adacore.com>

	* amd64-windows-tdep.c (struct x64_frame_cache): Declare.
	(x64_w2gdb_regnum): New array.
	(x64_frame_decode_epilogue,	x64_frame_decode_insns)
	(x64_frame_cache, x64_frame_prev_register, x64_frame_this_id): New
	functions.
	(x64_frame_unwind): New variable.
	(amd64_windows_init_abi): Register this unwinder.

diff --git a/gdb/amd64-windows-tdep.c b/gdb/amd64-windows-tdep.c
index 4a40f47..7070e06 100644
--- a/gdb/amd64-windows-tdep.c
+++ b/gdb/amd64-windows-tdep.c
@@ -23,6 +23,12 @@
 #include "gdbtypes.h"
 #include "gdbcore.h"
 #include "regcache.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[] =
@@ -153,6 +159,588 @@ amd64_skip_main_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
   return pc;
 }
 
+struct x64_frame_cache
+{
+  CORE_ADDR image_base;		/* ImageBase for the module.  */
+  CORE_ADDR start_address;	/* Function start rva.  */
+  CORE_ADDR pc;			/* Next instruction to be executed.  */
+  CORE_ADDR sp;			/* Current sp.  */
+
+  CORE_ADDR prev_reg_addr[16];	/* Address of saved registers.  */
+  CORE_ADDR prev_xmm_addr[16];	/* Likewise for xmm registers.  */
+  /* These two next fields are set only for machine info frames.  */
+  CORE_ADDR prev_rip_addr;	/* Likewise for RIP.  */
+  CORE_ADDR prev_rsp_addr;	/* Likewise for RSP.  */
+  CORE_ADDR prev_sp;		/* Address of the previous frame.  */
+};
+
+/* Convert a Windows register number to gdb.  */
+static const enum amd64_regnum x64_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.  */
+
+static int
+x64_frame_decode_epilogue (struct frame_info *this_frame,
+			   struct x64_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 that deallocate 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[x64_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[x64_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, x64_w2gdb_regnum[reg], buf);
+	  pc = extract_unsigned_integer (buf, 8, byte_order);
+	}
+      else
+	return 0;
+
+      /* Allow the user to break this loop.  */
+      if (quit_flag)
+	return 0;
+    }
+}
+
+/* Decode and execute unwind insns at UNWIND_INFO.  */
+
+static void
+x64_frame_decode_insns (struct frame_info *this_frame,
+			struct x64_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;
+      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,
+			      (char *) &ex_ui, sizeof (ex_ui)) != 0)
+	return;
+
+      if (unwind_debug)
+	fprintf_unfiltered
+	  (gdb_stdlog,
+	   "x64_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_address + ex_ui.SizeOfPrologue))
+	{
+	  /* Not in the prologue; try to decode an epilog.  */
+	  if (x64_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 the the FP reg is established in the prolog.  */
+	  gdb_byte buf[8];
+	  int frreg = x64_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_address + 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 = x64_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 = x64_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 = x64_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
+		    + 8 * 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
+		    + 8 * 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, (char *) &d, sizeof (d)) != 0)
+	    return;
+
+	  cache->start_address =
+	    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.  */
+      if (quit_flag)
+	return;
+    }
+  /* 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));
+}
+
+static struct x64_frame_cache *
+x64_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 x64_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;
+
+  if (*this_cache)
+    return *this_cache;
+
+  cache = FRAME_OBSTACK_ZALLOC (struct x64_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;
+
+  /* Get the corresponding exception directory.  */
+  sec = find_pc_section (pc);
+  if (unwind_debug)
+    fprintf_unfiltered (gdb_stdlog, "x64_frame_cache: pc=%s, sp=%s, sec=%p\n",
+			paddress (gdbarch, pc),
+			paddress (gdbarch, cache->sp), sec);
+  if (sec == NULL)
+    return cache;
+  objfile = sec->objfile;
+  pe = pe_data (sec->objfile->obfd);
+  dir = &pe->pe_opthdr.DataDirectory[PE_EXCEPTION_TABLE];
+
+  image_base = pe->pe_opthdr.ImageBase
+    + ANOFFSET (objfile->section_offsets, SECT_OFF_TEXT (objfile));
+  cache->image_base = image_base;
+
+  if (unwind_debug)
+    fprintf_unfiltered (gdb_stdlog,
+			"x64_frame_cache:  file:%s, image_base=%s\n",
+			objfile->name, paddress (gdbarch, image_base));
+
+  /* Find the entry.
+     Note: it might be a better idea to ask directly to the kernel.  This
+     will handle dynamically added entries (for JIT engines).  */
+  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
+	  (image_base + dir->VirtualAddress + mid * sizeof (d),
+	   (char *) &d, sizeof (d)) != 0)
+	return cache;
+
+      sa = extract_unsigned_integer (d.rva_BeginAddress, 4, byte_order);
+      ea = extract_unsigned_integer (d.rva_EndAddress, 4, byte_order);
+      if (pc < image_base + sa)
+	hi = mid - 1;
+      else if (pc >= image_base + ea)
+	lo = mid + 1;
+      else if (pc >= image_base + sa && pc < image_base + ea)
+	{
+	  /* Got it.  */
+	  cache->start_address = sa;
+	  unwind_info =
+	    extract_unsigned_integer (d.rva_UnwindData, 4, byte_order);
+	  break;
+	}
+      else
+	break;
+    }
+
+  if (unwind_debug)
+    fprintf_unfiltered (gdb_stdlog,
+			"x64_frame_cache:  image_base=%s, unwind_data=%s\n",
+			paddress (gdbarch, cache->image_base),
+			paddress (gdbarch, unwind_info));
+
+  if (unwind_info == 0)
+    {
+      /* Assume a leaf function.  */
+      cache->prev_sp = cache->sp + 8;
+      cache->prev_rip_addr = cache->sp;
+    }
+  else
+    {
+      if (unwind_info & 1)
+	{
+	  /* Unofficially documented unwind info redirection.  */
+	  struct external_pex64_runtime_function d;
+	  CORE_ADDR sa, ea;
+
+	  if (target_read_memory (image_base + (unwind_info & ~1),
+				  (char *) &d, sizeof (d)) != 0)
+	    return cache;
+
+	  cache->start_address =
+	    extract_unsigned_integer (d.rva_BeginAddress, 4, byte_order);
+	  unwind_info =
+	    extract_unsigned_integer (d.rva_EndAddress, 4, byte_order);
+	}
+
+      /* Decode unwind insns to compute saved addresses.  */
+      x64_frame_decode_insns (this_frame, cache, unwind_info);
+    }
+  return cache;
+}
+
+static struct value *
+x64_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 x64_frame_cache *cache = x64_frame_cache (this_frame, this_cache);
+  struct value *val;
+  CORE_ADDR prev;
+
+  if (unwind_debug)
+    fprintf_unfiltered (gdb_stdlog, "x64_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)
+    return frame_unwind_got_memory (this_frame, regnum, prev);
+  else
+    return frame_unwind_got_register (this_frame, regnum, regnum);
+}
+
+static void
+x64_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 x64_frame_cache *cache = x64_frame_cache (this_frame, this_cache);
+
+  *this_id = frame_id_build (cache->prev_sp,
+			     cache->image_base + cache->start_address);
+}
+
+/* x64 SEH unwinder.  */
+
+static const struct frame_unwind x64_frame_unwind =
+{
+  NORMAL_FRAME,
+  default_frame_unwind_stop_reason,
+  &x64_frame_this_id,
+  &x64_frame_prev_register,
+  NULL,
+  default_frame_sniffer
+};
 
 static void
 amd64_windows_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
@@ -174,6 +762,8 @@ amd64_windows_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
   set_gdbarch_return_value (gdbarch, amd64_windows_return_value);
   set_gdbarch_skip_main_prologue (gdbarch, amd64_skip_main_prologue);
 
+  frame_unwind_prepend_unwinder (gdbarch, &x64_frame_unwind);
+
   set_solib_ops (gdbarch, &solib_target_so_ops);
 }
 


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