+ /* 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 (frame_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,
+ CORE_ADDR *end_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;
+ *end_rva = ea;
+ *unwind_info =
+ extract_unsigned_integer (d.rva_UnwindData, 4, byte_order);
+ break;
+ }
+ else
+ break;
+ }
+
+ if (frame_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 (http://www.codemachine.com/article_x64deepdive.html).
+ */
+ 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);
+ *end_rva = extract_unsigned_integer (d.rva_EndAddress, 4, byte_order);
+ *unwind_info =
+ extract_unsigned_integer (d.rva_UnwindData, 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;
+ gdb_byte 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,
+ &cache->end_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 (frame_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 && frame_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, end_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, &end_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;
+}
+
+/* Check Win64 DLL jmp trampolines and find jump destination. */
+
+static CORE_ADDR
+amd64_windows_skip_trampoline_code (struct frame_info *frame, CORE_ADDR pc)
+{
+ CORE_ADDR destination = 0;
+ struct gdbarch *gdbarch = get_frame_arch (frame);
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+
+ /* Check for jmp *<offset>(%rip) (jump near, absolute indirect (/4)). */
+ if (pc && read_memory_unsigned_integer (pc, 2, byte_order) == 0x25ff)
+ {
+ /* Get opcode offset and see if we can find a reference in our data. */
+ ULONGEST offset
+ = read_memory_unsigned_integer (pc + 2, 4, byte_order);
+
+ /* Get address of function pointer at end of pc. */
+ CORE_ADDR indirect_addr = pc + offset + 6;
+
+ struct minimal_symbol *indsym
+ = (indirect_addr
+ ? lookup_minimal_symbol_by_pc (indirect_addr).minsym
+ : NULL);
+ const char *symname = indsym ? MSYMBOL_LINKAGE_NAME (indsym) : NULL;
+
+ if (symname)
+ {
+ if (strncmp (symname, "__imp_", 6) == 0
+ || strncmp (symname, "_imp_", 5) == 0)
+ destination
+ = read_memory_unsigned_integer (indirect_addr, 8, byte_order);
+ }
+ }
+
+ return destination;
+}
+
+/* Implement the "auto_wide_charset" gdbarch method. */
+
+static const char *
+amd64_windows_auto_wide_charset (void)
+{
+ return "UTF-16";