From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 7809 invoked by alias); 18 Jun 2012 09:18:07 -0000 Received: (qmail 7787 invoked by uid 22791); 18 Jun 2012 09:18:04 -0000 X-SWARE-Spam-Status: No, hits=-2.5 required=5.0 tests=AWL,BAYES_00,KHOP_THREADED,TW_BJ,TW_QW X-Spam-Check-By: sourceware.org Received: from mel.act-europe.fr (HELO mel.act-europe.fr) (194.98.77.210) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Mon, 18 Jun 2012 09:17:49 +0000 Received: from localhost (localhost [127.0.0.1]) by filtered-smtp.eu.adacore.com (Postfix) with ESMTP id B90E9290008; Mon, 18 Jun 2012 11:17:53 +0200 (CEST) Received: from mel.act-europe.fr ([127.0.0.1]) by localhost (smtp.eu.adacore.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id dle9wAm2Qgbh; Mon, 18 Jun 2012 11:17:53 +0200 (CEST) Received: from ulanbator.act-europe.fr (ulanbator.act-europe.fr [10.10.1.67]) (using TLSv1 with cipher AES128-SHA (128/128 bits)) (No client certificate requested) by mel.act-europe.fr (Postfix) with ESMTP id 8472C290007; Mon, 18 Jun 2012 11:17:53 +0200 (CEST) Subject: Re: [RFA] Add Windows x64 SEH unwinder Mime-Version: 1.0 (Apple Message framework v1278) Content-Type: text/plain; charset=us-ascii From: Tristan Gingold In-Reply-To: <201206161039.q5GAdfST016307@glazunov.sibelius.xs4all.nl> Date: Mon, 18 Jun 2012 09:18:00 -0000 Cc: "gdb-patches@sourceware.org ml" , Kai Tietz , Mark Kettenis Content-Transfer-Encoding: quoted-printable Message-Id: <8A4BC294-364D-4D09-8C24-B492A9340C9D@adacore.com> References: <201206161039.q5GAdfST016307@glazunov.sibelius.xs4all.nl> To: Joel Brobecker X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2012-06/txt/msg00572.txt.bz2 On Jun 16, 2012, at 12:39 PM, Mark Kettenis wrote: >> From: Tristan Gingold >> Date: Fri, 15 Jun 2012 10:09:56 +0200 >>=20 >> Hi, >>=20 >> 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 >>=20 >> 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). >>=20 >> 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. >>=20 >> Comments are welcome. >=20 > The amd64 codebase consistently uses the amd64_-prefix for structures, > variables and functions. If this is effectively *the* unwinder for > 64-bit windows, I'd recommend "amd64_windows_". If you expect more > Windows-specfic unwinders (or think amd64_windows_ is too long) > "amd64_seh_" might be a good alternative. Thanks for the comments. Here is the new version. Tristan. 2012-06-15 Tristan Gingold * 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. * NEWS: Mention it. diff --git a/gdb/NEWS b/gdb/NEWS index 2384489..34903a2 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -117,6 +117,8 @@ * GDB can now support 'breakpoint always-inserted mode' in 'record' target. =20 +* GDB can now use Windows x64 unwinding data. + * MI changes =20 ** New command -info-os is the MI equivalent of "info os". diff --git a/gdb/amd64-windows-tdep.c b/gdb/amd64-windows-tdep.c index 4a40f47..a16cf42 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" =20 /* The registers used to pass integer arguments during a function call. */ static int amd64_windows_dummy_call_integer_regs[] =3D @@ -153,6 +159,620 @@ amd64_skip_main_prologue (struct gdbarch *gdbarch, CO= RE_ADDR pc) return pc; } =20 +struct amd64_windows_frame_cache +{ + /* ImageBase for the module. */ + CORE_ADDR image_base; + + /* Function start rva. */ + CORE_ADDR start_address; + + /* 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[] =3D +{ + 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 +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 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 =3D cache->pc; + CORE_ADDR cur_sp =3D cache->sp; + struct gdbarch *gdbarch =3D get_frame_arch (this_frame); + enum bfd_endian byte_order =3D gdbarch_byte_order (gdbarch); + + while (1) + { + gdb_byte op; + gdb_byte rex; + + if (target_read_memory (pc, &op, 1) !=3D 0) + return -1; + + if (op =3D=3D 0xc3) + { + /* Ret. */ + cache->prev_rip_addr =3D cur_sp; + cache->prev_sp =3D cur_sp + 8; + return 1; + } + else if (op =3D=3D 0xeb) + { + /* jmp rel8 */ + gdb_byte rel8; + + if (target_read_memory (pc + 1, &rel8, 1) !=3D 0) + return -1; + pc =3D pc + 2 + (signed char)rel8; + } + else if (op =3D=3D 0xec) + { + /* jmp rel32 */ + gdb_byte rel32[4]; + + if (target_read_memory (pc + 1, rel32, 4) !=3D 0) + return -1; + pc =3D pc + 5 + extract_signed_integer (rel32, 4, byte_order); + } + else if (op >=3D 0x58 && op <=3D 0x5f) + { + /* pop reg */ + cache->prev_reg_addr[amd64_windows_w2gdb_regnum[op & 0x0f]] =3D cur_sp; + cur_sp +=3D 8; + } + else if (op =3D=3D 0xc2) + { + /* ret n */ + gdb_byte imm16[2]; + + if (target_read_memory (pc + 1, imm16, 2) !=3D 0) + return -1; + cache->prev_rip_addr =3D cur_sp; + cache->prev_sp =3D cur_sp + + extract_unsigned_integer (imm16, 4, byte_order); + return 1; + } + else if (op =3D=3D 0xf3) + { + /* rep; ret */ + gdb_byte op1; + + if (target_read_memory (pc + 2, &op1, 1) !=3D 0) + return -1; + if (op1 !=3D 0xc3) + return 0; + + cache->prev_rip_addr =3D cur_sp; + cache->prev_sp =3D 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 =3D op; + if (target_read_memory (pc + 1, &op, 1) !=3D 0) + return -1; + + if (op >=3D 0x58 && op <=3D 0x5f) + { + /* pop reg */ + unsigned int reg; + + reg =3D (op & 0x0f) | ((rex & 1) << 3); + cache->prev_reg_addr[amd64_windows_w2gdb_regnum[reg]] =3D cur_sp; + cur_sp +=3D 8; + } + else if (op =3D=3D 0xff) + { + /* rex jmp reg */ + gdb_byte op1; + unsigned int reg; + gdb_byte buf[8]; + + if (target_read_memory (pc + 2, &op1, 1) !=3D 0) + return -1; + if ((op1 & 0xf8) !=3D 0xe0) + return 0; + reg =3D (op1 & 0x0f) | ((rex & 1) << 3); + + get_frame_register (this_frame, + amd64_windows_w2gdb_regnum[reg], buf); + pc =3D 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 =3D 0; + CORE_ADDR cur_sp =3D cache->sp; + struct gdbarch *gdbarch =3D get_frame_arch (this_frame); + enum bfd_endian byte_order =3D gdbarch_byte_order (gdbarch); + int j; + + for (j =3D 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)) !=3D 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) !=3D 1) + return; + + if (j =3D=3D 0 + && (cache->pc >=3D + cache->image_base + cache->start_address + ex_ui.SizeOfPrologue)) + { + /* Not in the prologue; try to decode an epilog. */ + if (amd64_windows_frame_decode_epilogue (this_frame, cache) =3D=3D 1) + return; + + /* Not in an epilog. Clear possible side effects. */ + memset (cache->prev_reg_addr, 0, sizeof (cache->prev_reg_addr)); + } + + codes_count =3D ex_ui.CountOfCodes; + frame_reg =3D PEX64_UWI_FRAMEREG (ex_ui.FrameRegisterOffset); + + if (frame_reg !=3D 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 =3D amd64_windows_w2gdb_regnum[frame_reg]; + + get_frame_register (this_frame, frreg, buf); + save_addr =3D extract_unsigned_integer (buf, 8, byte_order); + + if (unwind_debug) + fprintf_unfiltered (gdb_stdlog, " frame_reg=3D%s, val=3D%s\n", + gdbarch_register_name (gdbarch, frreg), + paddress (gdbarch, save_addr)); + } + + /* Read opcodes. */ + if (codes_count !=3D 0 + && target_read_memory (cache->image_base + unwind_info + + sizeof (ex_ui), + insns, codes_count * 2) !=3D 0) + return; + + end_insns =3D &insns[codes_count * 2]; + for (p =3D insns; p < end_insns; p +=3D 2) + { + int reg; + + if (unwind_debug) + fprintf_unfiltered + (gdb_stdlog, " op #%u: off=3D0x%02x, insn=3D0x%02x\n", + (unsigned) (p - insns), p[0], p[1]); + + /* Virtually execute the operation. */ + if (cache->pc >=3D 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 =3D=3D 0) + save_addr =3D cur_sp; + + switch (PEX64_UNWCODE_CODE (p[1])) + { + case UWOP_PUSH_NONVOL: + /* Push pre-decrements RSP. */ + reg =3D amd64_windows_w2gdb_regnum[PEX64_UNWCODE_INFO (p[1])]; + cache->prev_reg_addr[reg] =3D cur_sp; + cur_sp +=3D 8; + break; + case UWOP_ALLOC_LARGE: + if (PEX64_UNWCODE_INFO (p[1]) =3D=3D 0) + cur_sp +=3D + 8 * extract_unsigned_integer (p + 2, 2, byte_order); + else if (PEX64_UNWCODE_INFO (p[1]) =3D=3D 1) + cur_sp +=3D extract_unsigned_integer (p + 2, 4, byte_order); + else + return; + break; + case UWOP_ALLOC_SMALL: + cur_sp +=3D 8 + 8 * PEX64_UNWCODE_INFO (p[1]); + break; + case UWOP_SET_FPREG: + cur_sp =3D save_addr + - PEX64_UWI_FRAMEOFF (ex_ui.FrameRegisterOffset) * 16; + break; + case UWOP_SAVE_NONVOL: + reg =3D amd64_windows_w2gdb_regnum[PEX64_UNWCODE_INFO (p[1])]; + cache->prev_reg_addr[reg] =3D save_addr + + 8 * extract_unsigned_integer (p + 2, 2, byte_order); + break; + case UWOP_SAVE_NONVOL_FAR: + reg =3D amd64_windows_w2gdb_regnum[PEX64_UNWCODE_INFO (p[1])]; + cache->prev_reg_addr[reg] =3D 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])] =3D + 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])] =3D + save_addr + + 8 * extract_unsigned_integer (p + 2, 4, byte_order); + break; + case UWOP_PUSH_MACHFRAME: + if (PEX64_UNWCODE_INFO (p[1]) =3D=3D 0) + { + cache->prev_rip_addr =3D cur_sp + 0; + cache->prev_rsp_addr =3D cur_sp + 24; + cur_sp +=3D 40; + } + else if (PEX64_UNWCODE_INFO (p[1]) =3D=3D 1) + { + cache->prev_rip_addr =3D cur_sp + 8; + cache->prev_rsp_addr =3D cur_sp + 32; + cur_sp +=3D 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]) =3D=3D 0) + p +=3D 2; + else if (PEX64_UNWCODE_INFO (p[1]) =3D=3D 1) + p +=3D 4; + else + return; + break; + case UWOP_SAVE_NONVOL: + case UWOP_SAVE_XMM128: + p +=3D 2; + break; + case UWOP_SAVE_NONVOL_FAR: + case UWOP_SAVE_XMM128_FAR: + p +=3D 4; + break; + default: + return; + } + } + if (PEX64_UWI_FLAGS (ex_ui.Version_Flags) !=3D UNW_FLAG_CHAININFO) + break; + else + { + /* Read the chained unwind info. */ + struct external_pex64_runtime_function d; + CORE_ADDR chain_vma; + + chain_vma =3D 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)) !=3D 0) + return; + + cache->start_address =3D + extract_unsigned_integer (d.rva_BeginAddress, 4, byte_order); + unwind_info =3D + 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 =3D=3D 0) + cache->prev_rip_addr =3D cur_sp; + cache->prev_sp =3D 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)); +} + +/* Do all the work: fill THIS_CACHE with unwinding data for THIS_FRAME. */ + +static struct amd64_windows_frame_cache * +amd64_windows_frame_cache (struct frame_info *this_frame, void **this_cach= e) +{ + struct gdbarch *gdbarch =3D get_frame_arch (this_frame); + enum bfd_endian byte_order =3D 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; + + if (*this_cache) + return *this_cache; + + cache =3D FRAME_OBSTACK_ZALLOC (struct amd64_windows_frame_cache); + *this_cache =3D cache; + + /* Get current PC and SP. */ + pc =3D get_frame_pc (this_frame); + get_frame_register (this_frame, AMD64_RSP_REGNUM, buf); + cache->sp =3D extract_unsigned_integer (buf, 8, byte_order); + cache->pc =3D pc; + + /* Get the corresponding exception directory. */ + sec =3D find_pc_section (pc); + if (unwind_debug) + fprintf_unfiltered (gdb_stdlog, + "amd64_windows_frame_cache: pc=3D%s, sp=3D%s, sec=3D%p\n", + paddress (gdbarch, pc), + paddress (gdbarch, cache->sp), sec); + if (sec =3D=3D NULL) + return cache; + objfile =3D sec->objfile; + pe =3D pe_data (sec->objfile->obfd); + dir =3D &pe->pe_opthdr.DataDirectory[PE_EXCEPTION_TABLE]; + + image_base =3D pe->pe_opthdr.ImageBase + + ANOFFSET (objfile->section_offsets, SECT_OFF_TEXT (objfile)); + cache->image_base =3D image_base; + + if (unwind_debug) + fprintf_unfiltered (gdb_stdlog, + "amd64_windows_frame_cache: file:%s, image_base=3D%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 =3D 0; + hi =3D dir->Size / sizeof (struct external_pex64_runtime_function); + unwind_info =3D 0; + while (lo <=3D hi) + { + unsigned long mid =3D 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)) !=3D 0) + return cache; + + sa =3D extract_unsigned_integer (d.rva_BeginAddress, 4, byte_order); + ea =3D extract_unsigned_integer (d.rva_EndAddress, 4, byte_order); + if (pc < image_base + sa) + hi =3D mid - 1; + else if (pc >=3D image_base + ea) + lo =3D mid + 1; + else if (pc >=3D image_base + sa && pc < image_base + ea) + { + /* Got it. */ + cache->start_address =3D sa; + unwind_info =3D + extract_unsigned_integer (d.rva_UnwindData, 4, byte_order); + break; + } + else + break; + } + + if (unwind_debug) + fprintf_unfiltered + (gdb_stdlog, + "amd64_windows_frame_cache: image_base=3D%s, unwind_data=3D%s\n", + paddress (gdbarch, cache->image_base), paddress (gdbarch, unwind_in= fo)); + + if (unwind_info =3D=3D 0) + { + /* Assume a leaf function. */ + cache->prev_sp =3D cache->sp + 8; + cache->prev_rip_addr =3D 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)) !=3D 0) + return cache; + + cache->start_address =3D + extract_unsigned_integer (d.rva_BeginAddress, 4, byte_order); + unwind_info =3D + extract_unsigned_integer (d.rva_EndAddress, 4, byte_order); + } + + /* Decode unwind insns to compute saved addresses. */ + amd64_windows_frame_decode_insns (this_frame, cache, unwind_info); + } + return cache; +} + +/* Get the value of register REGNUM before the execution of code for + THIS_FRAME, using THIS_CACHE data. */ + +static struct value * +amd64_windows_frame_prev_register (struct frame_info *this_frame, + void **this_cache, int regnum) +{ + struct gdbarch *gdbarch =3D get_frame_arch (this_frame); + enum bfd_endian byte_order =3D gdbarch_byte_order (gdbarch); + struct amd64_windows_frame_cache *cache =3D + 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=3D%s\n", + gdbarch_register_name (gdbarch, regnum), + paddress (gdbarch, cache->prev_sp)); + + if (regnum >=3D AMD64_XMM0_REGNUM && regnum <=3D AMD64_XMM0_REGNUM + 15) + prev =3D cache->prev_xmm_addr[regnum - AMD64_XMM0_REGNUM]; + else if (regnum =3D=3D AMD64_RSP_REGNUM) + { + prev =3D cache->prev_rsp_addr; + if (prev =3D=3D 0) + return frame_unwind_got_constant (this_frame, regnum, cache->prev_sp); + } + else if (regnum >=3D AMD64_RAX_REGNUM && regnum <=3D AMD64_R15_REGNUM) + prev =3D cache->prev_reg_addr[regnum - AMD64_RAX_REGNUM]; + else if (regnum =3D=3D AMD64_RIP_REGNUM) + prev =3D cache->prev_rip_addr; + else + prev =3D 0; + + if (prev && unwind_debug) + fprintf_unfiltered (gdb_stdlog, " -> at %s\n", paddress (gdbarch, pre= v)); + + 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); + } +} + +/* Compute and store the frame identifier to THIS_ID for THIS_FRAME using + cached data in THIS_CACHE. */ + +static void +amd64_windows_frame_this_id (struct frame_info *this_frame, void **this_ca= che, + struct frame_id *this_id) +{ + struct gdbarch *gdbarch =3D get_frame_arch (this_frame); + struct amd64_windows_frame_cache *cache =3D + amd64_windows_frame_cache (this_frame, this_cache); + + *this_id =3D frame_id_build (cache->prev_sp, + cache->image_base + cache->start_address); +} + +/* Windows x64 SEH unwinder. */ + +static const struct frame_unwind amd64_windows_frame_unwind =3D +{ + NORMAL_FRAME, + default_frame_unwind_stop_reason, + &amd64_windows_frame_this_id, + &amd64_windows_frame_prev_register, + NULL, + default_frame_sniffer +}; =20 static void amd64_windows_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) @@ -174,6 +794,8 @@ amd64_windows_init_abi (struct gdbarch_info info, struc= t gdbarch *gdbarch) set_gdbarch_return_value (gdbarch, amd64_windows_return_value); set_gdbarch_skip_main_prologue (gdbarch, amd64_skip_main_prologue); =20 + frame_unwind_prepend_unwinder (gdbarch, &amd64_windows_frame_unwind); + set_solib_ops (gdbarch, &solib_target_so_ops); }