+ /* Skip legacy instruction prefixes. */
+ insn = amd64_skip_prefixes (insn);
+
+ /* Skip REX/VEX instruction encoding prefixes. */
+ if (rex_prefix_p (*insn))
+ {
+ details->enc_prefix_offset = insn - start;
+ ++insn;
+ }
+ else if (vex2_prefix_p (*insn))
+ {
+ /* Don't record the offset in this case because this prefix has
+ no REX.B equivalent. */
+ insn += 2;
+ }
+ else if (vex3_prefix_p (*insn))
+ {
+ details->enc_prefix_offset = insn - start;
+ insn += 3;
+ }
+
+ details->opcode_offset = insn - start;
+
+ if (*insn == TWO_BYTE_OPCODE_ESCAPE)
+ {
+ /* Two or three-byte opcode. */
+ ++insn;
+ need_modrm = twobyte_has_modrm[*insn];
+
+ /* Check for three-byte opcode. */
+ switch (*insn)
+ {
+ case 0x24:
+ case 0x25:
+ case 0x38:
+ case 0x3a:
+ case 0x7a:
+ case 0x7b:
+ ++insn;
+ details->opcode_len = 3;
+ break;
+ default:
+ details->opcode_len = 2;
+ break;
+ }
+ }
+ else
+ {
+ /* One-byte opcode. */
+ need_modrm = onebyte_has_modrm[*insn];
+ details->opcode_len = 1;
+ }
+
+ if (need_modrm)
+ {
+ ++insn;
+ details->modrm_offset = insn - start;
+ }
+}
+
+/* Update %rip-relative addressing in INSN.
+
+ %rip-relative addressing only uses a 32-bit displacement.
+ 32 bits is not enough to be guaranteed to cover the distance between where
+ the real instruction is and where its copy is.
+ Convert the insn to use base+disp addressing.
+ We set base = pc + insn_length so we can leave disp unchanged. */
+
+static void
+fixup_riprel (struct gdbarch *gdbarch, amd64_displaced_step_copy_insn_closure *dsc,
+ CORE_ADDR from, CORE_ADDR to, struct regcache *regs)
+{
+ const struct amd64_insn *insn_details = &dsc->insn_details;
+ int modrm_offset = insn_details->modrm_offset;
+ gdb_byte *insn = insn_details->raw_insn + modrm_offset;
+ CORE_ADDR rip_base;
+ int insn_length;
+ int arch_tmp_regno, tmp_regno;
+ ULONGEST orig_value;
+
+ /* %rip+disp32 addressing mode, displacement follows ModRM byte. */
+ ++insn;
+
+ /* Compute the rip-relative address. */
+ insn_length = gdb_buffered_insn_length (gdbarch, dsc->insn_buf.data (),
+ dsc->insn_buf.size (), from);
+ rip_base = from + insn_length;
+
+ /* We need a register to hold the address.
+ Pick one not used in the insn.
+ NOTE: arch_tmp_regno uses architecture ordering, e.g. RDI = 7. */
+ arch_tmp_regno = amd64_get_unused_input_int_reg (insn_details);
+ tmp_regno = amd64_arch_reg_to_regnum (arch_tmp_regno);
+
+ /* Position of the not-B bit in the 3-byte VEX prefix (in byte 1). */
+ static constexpr gdb_byte VEX3_NOT_B = 0x20;
+
+ /* REX.B should be unset (VEX.!B set) as we were using rip-relative
+ addressing, but ensure it's unset (set for VEX) anyway, tmp_regno
+ is not r8-r15. */
+ if (insn_details->enc_prefix_offset != -1)
+ {
+ gdb_byte *pfx = &dsc->insn_buf[insn_details->enc_prefix_offset];
+ if (rex_prefix_p (pfx[0]))
+ pfx[0] &= ~REX_B;
+ else if (vex3_prefix_p (pfx[0]))
+ pfx[1] |= VEX3_NOT_B;
+ else
+ gdb_assert_not_reached ("unhandled prefix");
+ }
+
+ regcache_cooked_read_unsigned (regs, tmp_regno, &orig_value);
+ dsc->tmp_regno = tmp_regno;
+ dsc->tmp_save = orig_value;
+ dsc->tmp_used = 1;
+
+ /* Convert the ModRM field to be base+disp. */
+ dsc->insn_buf[modrm_offset] &= ~0xc7;
+ dsc->insn_buf[modrm_offset] |= 0x80 + arch_tmp_regno;
+
+ regcache_cooked_write_unsigned (regs, tmp_regno, rip_base);
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: %%rip-relative addressing used.\n"
+ "displaced: using temp reg %d, old value %s, new value %s\n",
+ dsc->tmp_regno, paddress (gdbarch, dsc->tmp_save),
+ paddress (gdbarch, rip_base));
+}
+
+static void
+fixup_displaced_copy (struct gdbarch *gdbarch,
+ amd64_displaced_step_copy_insn_closure *dsc,
+ CORE_ADDR from, CORE_ADDR to, struct regcache *regs)
+{
+ const struct amd64_insn *details = &dsc->insn_details;
+
+ if (details->modrm_offset != -1)
+ {
+ gdb_byte modrm = details->raw_insn[details->modrm_offset];
+
+ if ((modrm & 0xc7) == 0x05)
+ {
+ /* The insn uses rip-relative addressing.
+ Deal with it. */
+ fixup_riprel (gdbarch, dsc, from, to, regs);
+ }
+ }
+}
+
+displaced_step_copy_insn_closure_up
+amd64_displaced_step_copy_insn (struct gdbarch *gdbarch,
+ CORE_ADDR from, CORE_ADDR to,
+ struct regcache *regs)
+{
+ int len = gdbarch_max_insn_length (gdbarch);
+ /* Extra space for sentinels so fixup_{riprel,displaced_copy} don't have to
+ continually watch for running off the end of the buffer. */
+ int fixup_sentinel_space = len;
+ std::unique_ptr<amd64_displaced_step_copy_insn_closure> dsc
+ (new amd64_displaced_step_copy_insn_closure (len + fixup_sentinel_space));
+ gdb_byte *buf = &dsc->insn_buf[0];
+ struct amd64_insn *details = &dsc->insn_details;
+
+ read_memory (from, buf, len);
+
+ /* Set up the sentinel space so we don't have to worry about running
+ off the end of the buffer. An excessive number of leading prefixes
+ could otherwise cause this. */
+ memset (buf + len, 0, fixup_sentinel_space);
+
+ amd64_get_insn_details (buf, details);
+
+ /* GDB may get control back after the insn after the syscall.
+ Presumably this is a kernel bug.
+ If this is a syscall, make sure there's a nop afterwards. */
+ {
+ int syscall_length;
+
+ if (amd64_syscall_p (details, &syscall_length))
+ buf[details->opcode_offset + syscall_length] = NOP_OPCODE;
+ }
+
+ /* Modify the insn to cope with the address where it will be executed from.
+ In particular, handle any rip-relative addressing. */
+ fixup_displaced_copy (gdbarch, dsc.get (), from, to, regs);
+
+ write_memory (to, buf, len);
+
+ if (debug_displaced)
+ {
+ fprintf_unfiltered (gdb_stdlog, "displaced: copy %s->%s: ",
+ paddress (gdbarch, from), paddress (gdbarch, to));
+ displaced_step_dump_bytes (gdb_stdlog, buf, len);
+ }
+
+ /* This is a work around for a problem with g++ 4.8. */
+ return displaced_step_copy_insn_closure_up (dsc.release ());
+}
+
+static int
+amd64_absolute_jmp_p (const struct amd64_insn *details)
+{
+ const gdb_byte *insn = &details->raw_insn[details->opcode_offset];
+
+ if (insn[0] == 0xff)
+ {
+ /* jump near, absolute indirect (/4) */
+ if ((insn[1] & 0x38) == 0x20)
+ return 1;
+
+ /* jump far, absolute indirect (/5) */
+ if ((insn[1] & 0x38) == 0x28)
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Return non-zero if the instruction DETAILS is a jump, zero otherwise. */
+
+static int
+amd64_jmp_p (const struct amd64_insn *details)
+{
+ const gdb_byte *insn = &details->raw_insn[details->opcode_offset];
+
+ /* jump short, relative. */
+ if (insn[0] == 0xeb)
+ return 1;
+
+ /* jump near, relative. */
+ if (insn[0] == 0xe9)
+ return 1;
+
+ return amd64_absolute_jmp_p (details);
+}
+
+static int
+amd64_absolute_call_p (const struct amd64_insn *details)
+{
+ const gdb_byte *insn = &details->raw_insn[details->opcode_offset];
+
+ if (insn[0] == 0xff)
+ {
+ /* Call near, absolute indirect (/2) */
+ if ((insn[1] & 0x38) == 0x10)
+ return 1;
+
+ /* Call far, absolute indirect (/3) */
+ if ((insn[1] & 0x38) == 0x18)
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+amd64_ret_p (const struct amd64_insn *details)
+{
+ /* NOTE: gcc can emit "repz ; ret". */
+ const gdb_byte *insn = &details->raw_insn[details->opcode_offset];
+
+ switch (insn[0])
+ {
+ case 0xc2: /* ret near, pop N bytes */
+ case 0xc3: /* ret near */
+ case 0xca: /* ret far, pop N bytes */
+ case 0xcb: /* ret far */
+ case 0xcf: /* iret */
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+static int
+amd64_call_p (const struct amd64_insn *details)
+{
+ const gdb_byte *insn = &details->raw_insn[details->opcode_offset];
+
+ if (amd64_absolute_call_p (details))
+ return 1;
+
+ /* call near, relative */
+ if (insn[0] == 0xe8)
+ return 1;
+
+ return 0;
+}
+
+/* Return non-zero if INSN is a system call, and set *LENGTHP to its
+ length in bytes. Otherwise, return zero. */
+
+static int
+amd64_syscall_p (const struct amd64_insn *details, int *lengthp)
+{
+ const gdb_byte *insn = &details->raw_insn[details->opcode_offset];
+
+ if (insn[0] == 0x0f && insn[1] == 0x05)
+ {
+ *lengthp = 2;
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Classify the instruction at ADDR using PRED.
+ Throw an error if the memory can't be read. */
+
+static int
+amd64_classify_insn_at (struct gdbarch *gdbarch, CORE_ADDR addr,
+ int (*pred) (const struct amd64_insn *))
+{
+ struct amd64_insn details;
+ gdb_byte *buf;
+ int len, classification;
+
+ len = gdbarch_max_insn_length (gdbarch);
+ buf = (gdb_byte *) alloca (len);
+
+ read_code (addr, buf, len);
+ amd64_get_insn_details (buf, &details);
+
+ classification = pred (&details);
+
+ return classification;
+}
+
+/* The gdbarch insn_is_call method. */
+
+static int
+amd64_insn_is_call (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+ return amd64_classify_insn_at (gdbarch, addr, amd64_call_p);
+}
+
+/* The gdbarch insn_is_ret method. */
+
+static int
+amd64_insn_is_ret (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+ return amd64_classify_insn_at (gdbarch, addr, amd64_ret_p);
+}
+
+/* The gdbarch insn_is_jump method. */
+
+static int
+amd64_insn_is_jump (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+ return amd64_classify_insn_at (gdbarch, addr, amd64_jmp_p);
+}
+
+/* Fix up the state of registers and memory after having single-stepped
+ a displaced instruction. */
+
+void
+amd64_displaced_step_fixup (struct gdbarch *gdbarch,
+ struct displaced_step_copy_insn_closure *dsc_,
+ CORE_ADDR from, CORE_ADDR to,
+ struct regcache *regs)
+{
+ amd64_displaced_step_copy_insn_closure *dsc = (amd64_displaced_step_copy_insn_closure *) dsc_;
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ /* The offset we applied to the instruction's address. */
+ ULONGEST insn_offset = to - from;
+ gdb_byte *insn = dsc->insn_buf.data ();
+ const struct amd64_insn *insn_details = &dsc->insn_details;
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog,
+ "displaced: fixup (%s, %s), "
+ "insn = 0x%02x 0x%02x ...\n",
+ paddress (gdbarch, from), paddress (gdbarch, to),
+ insn[0], insn[1]);
+
+ /* If we used a tmp reg, restore it. */
+
+ if (dsc->tmp_used)
+ {
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog, "displaced: restoring reg %d to %s\n",
+ dsc->tmp_regno, paddress (gdbarch, dsc->tmp_save));
+ regcache_cooked_write_unsigned (regs, dsc->tmp_regno, dsc->tmp_save);
+ }
+
+ /* The list of issues to contend with here is taken from
+ resume_execution in arch/x86/kernel/kprobes.c, Linux 2.6.28.
+ Yay for Free Software! */
+
+ /* Relocate the %rip back to the program's instruction stream,
+ if necessary. */
+
+ /* Except in the case of absolute or indirect jump or call
+ instructions, or a return instruction, the new rip is relative to
+ the displaced instruction; make it relative to the original insn.
+ Well, signal handler returns don't need relocation either, but we use the
+ value of %rip to recognize those; see below. */
+ if (! amd64_absolute_jmp_p (insn_details)
+ && ! amd64_absolute_call_p (insn_details)
+ && ! amd64_ret_p (insn_details))
+ {
+ ULONGEST orig_rip;
+ int insn_len;
+
+ regcache_cooked_read_unsigned (regs, AMD64_RIP_REGNUM, &orig_rip);
+
+ /* A signal trampoline system call changes the %rip, resuming
+ execution of the main program after the signal handler has
+ returned. That makes them like 'return' instructions; we
+ shouldn't relocate %rip.
+
+ But most system calls don't, and we do need to relocate %rip.
+
+ Our heuristic for distinguishing these cases: if stepping
+ over the system call instruction left control directly after
+ the instruction, the we relocate --- control almost certainly
+ doesn't belong in the displaced copy. Otherwise, we assume
+ the instruction has put control where it belongs, and leave
+ it unrelocated. Goodness help us if there are PC-relative
+ system calls. */
+ if (amd64_syscall_p (insn_details, &insn_len)
+ && orig_rip != to + insn_len
+ /* GDB can get control back after the insn after the syscall.
+ Presumably this is a kernel bug.
+ Fixup ensures its a nop, we add one to the length for it. */
+ && orig_rip != to + insn_len + 1)
+ {
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog,
+ "displaced: syscall changed %%rip; "
+ "not relocating\n");
+ }
+ else
+ {
+ ULONGEST rip = orig_rip - insn_offset;
+
+ /* If we just stepped over a breakpoint insn, we don't backup
+ the pc on purpose; this is to match behaviour without
+ stepping. */
+
+ regcache_cooked_write_unsigned (regs, AMD64_RIP_REGNUM, rip);
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog,
+ "displaced: "
+ "relocated %%rip from %s to %s\n",
+ paddress (gdbarch, orig_rip),
+ paddress (gdbarch, rip));
+ }
+ }
+
+ /* If the instruction was PUSHFL, then the TF bit will be set in the
+ pushed value, and should be cleared. We'll leave this for later,
+ since GDB already messes up the TF flag when stepping over a
+ pushfl. */
+
+ /* If the instruction was a call, the return address now atop the
+ stack is the address following the copied instruction. We need
+ to make it the address following the original instruction. */
+ if (amd64_call_p (insn_details))
+ {
+ ULONGEST rsp;
+ ULONGEST retaddr;
+ const ULONGEST retaddr_len = 8;
+
+ regcache_cooked_read_unsigned (regs, AMD64_RSP_REGNUM, &rsp);
+ retaddr = read_memory_unsigned_integer (rsp, retaddr_len, byte_order);
+ retaddr = (retaddr - insn_offset) & 0xffffffffffffffffULL;
+ write_memory_unsigned_integer (rsp, retaddr_len, byte_order, retaddr);
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog,
+ "displaced: relocated return addr at %s "
+ "to %s\n",
+ paddress (gdbarch, rsp),
+ paddress (gdbarch, retaddr));
+ }
+}
+
+/* If the instruction INSN uses RIP-relative addressing, return the
+ offset into the raw INSN where the displacement to be adjusted is
+ found. Returns 0 if the instruction doesn't use RIP-relative
+ addressing. */
+
+static int
+rip_relative_offset (struct amd64_insn *insn)
+{
+ if (insn->modrm_offset != -1)
+ {
+ gdb_byte modrm = insn->raw_insn[insn->modrm_offset];
+
+ if ((modrm & 0xc7) == 0x05)
+ {
+ /* The displacement is found right after the ModRM byte. */
+ return insn->modrm_offset + 1;
+ }
+ }
+
+ return 0;
+}
+
+static void
+append_insns (CORE_ADDR *to, ULONGEST len, const gdb_byte *buf)
+{
+ target_write_memory (*to, buf, len);
+ *to += len;
+}
+
+static void
+amd64_relocate_instruction (struct gdbarch *gdbarch,
+ CORE_ADDR *to, CORE_ADDR oldloc)
+{
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ int len = gdbarch_max_insn_length (gdbarch);
+ /* Extra space for sentinels. */
+ int fixup_sentinel_space = len;
+ gdb_byte *buf = (gdb_byte *) xmalloc (len + fixup_sentinel_space);
+ struct amd64_insn insn_details;
+ int offset = 0;
+ LONGEST rel32, newrel;
+ gdb_byte *insn;
+ int insn_length;
+
+ read_memory (oldloc, buf, len);
+
+ /* Set up the sentinel space so we don't have to worry about running
+ off the end of the buffer. An excessive number of leading prefixes
+ could otherwise cause this. */
+ memset (buf + len, 0, fixup_sentinel_space);
+
+ insn = buf;
+ amd64_get_insn_details (insn, &insn_details);
+
+ insn_length = gdb_buffered_insn_length (gdbarch, insn, len, oldloc);
+
+ /* Skip legacy instruction prefixes. */
+ insn = amd64_skip_prefixes (insn);
+
+ /* Adjust calls with 32-bit relative addresses as push/jump, with
+ the address pushed being the location where the original call in
+ the user program would return to. */
+ if (insn[0] == 0xe8)
+ {
+ gdb_byte push_buf[32];
+ CORE_ADDR ret_addr;
+ int i = 0;
+
+ /* Where "ret" in the original code will return to. */
+ ret_addr = oldloc + insn_length;
+
+ /* If pushing an address higher than or equal to 0x80000000,
+ avoid 'pushq', as that sign extends its 32-bit operand, which
+ would be incorrect. */
+ if (ret_addr <= 0x7fffffff)
+ {
+ push_buf[0] = 0x68; /* pushq $... */
+ store_unsigned_integer (&push_buf[1], 4, byte_order, ret_addr);
+ i = 5;
+ }
+ else
+ {
+ push_buf[i++] = 0x48; /* sub $0x8,%rsp */
+ push_buf[i++] = 0x83;
+ push_buf[i++] = 0xec;
+ push_buf[i++] = 0x08;
+
+ push_buf[i++] = 0xc7; /* movl $imm,(%rsp) */
+ push_buf[i++] = 0x04;
+ push_buf[i++] = 0x24;
+ store_unsigned_integer (&push_buf[i], 4, byte_order,
+ ret_addr & 0xffffffff);
+ i += 4;
+
+ push_buf[i++] = 0xc7; /* movl $imm,4(%rsp) */
+ push_buf[i++] = 0x44;
+ push_buf[i++] = 0x24;
+ push_buf[i++] = 0x04;
+ store_unsigned_integer (&push_buf[i], 4, byte_order,
+ ret_addr >> 32);
+ i += 4;
+ }
+ gdb_assert (i <= sizeof (push_buf));
+ /* Push the push. */
+ append_insns (to, i, push_buf);
+
+ /* Convert the relative call to a relative jump. */
+ insn[0] = 0xe9;
+
+ /* Adjust the destination offset. */
+ rel32 = extract_signed_integer (insn + 1, 4, byte_order);
+ newrel = (oldloc - *to) + rel32;
+ store_signed_integer (insn + 1, 4, byte_order, newrel);
+
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog,
+ "Adjusted insn rel32=%s at %s to"
+ " rel32=%s at %s\n",
+ hex_string (rel32), paddress (gdbarch, oldloc),
+ hex_string (newrel), paddress (gdbarch, *to));
+
+ /* Write the adjusted jump into its displaced location. */
+ append_insns (to, 5, insn);
+ return;
+ }
+
+ offset = rip_relative_offset (&insn_details);
+ if (!offset)
+ {
+ /* Adjust jumps with 32-bit relative addresses. Calls are
+ already handled above. */
+ if (insn[0] == 0xe9)
+ offset = 1;
+ /* Adjust conditional jumps. */
+ else if (insn[0] == 0x0f && (insn[1] & 0xf0) == 0x80)
+ offset = 2;
+ }
+
+ if (offset)
+ {
+ rel32 = extract_signed_integer (insn + offset, 4, byte_order);
+ newrel = (oldloc - *to) + rel32;
+ store_signed_integer (insn + offset, 4, byte_order, newrel);
+ if (debug_displaced)
+ fprintf_unfiltered (gdb_stdlog,
+ "Adjusted insn rel32=%s at %s to"
+ " rel32=%s at %s\n",
+ hex_string (rel32), paddress (gdbarch, oldloc),
+ hex_string (newrel), paddress (gdbarch, *to));
+ }
+
+ /* Write the adjusted instruction into its displaced location. */
+ append_insns (to, insn_length, buf);
+}
+
+\f
+/* The maximum number of saved registers. This should include %rip. */
+#define AMD64_NUM_SAVED_REGS AMD64_NUM_GREGS
+
+struct amd64_frame_cache
+{
+ /* Base address. */
+ CORE_ADDR base;
+ int base_p;
+ CORE_ADDR sp_offset;
+ CORE_ADDR pc;
+
+ /* Saved registers. */
+ CORE_ADDR saved_regs[AMD64_NUM_SAVED_REGS];
+ CORE_ADDR saved_sp;
+ int saved_sp_reg;
+
+ /* Do we have a frame? */
+ int frameless_p;
+};
+
+/* Initialize a frame cache. */
+
+static void
+amd64_init_frame_cache (struct amd64_frame_cache *cache)
+{
+ int i;
+
+ /* Base address. */
+ cache->base = 0;
+ cache->base_p = 0;
+ cache->sp_offset = -8;
+ cache->pc = 0;
+
+ /* Saved registers. We initialize these to -1 since zero is a valid
+ offset (that's where %rbp is supposed to be stored).
+ The values start out as being offsets, and are later converted to
+ addresses (at which point -1 is interpreted as an address, still meaning
+ "invalid"). */
+ for (i = 0; i < AMD64_NUM_SAVED_REGS; i++)
+ cache->saved_regs[i] = -1;
+ cache->saved_sp = 0;
+ cache->saved_sp_reg = -1;
+
+ /* Frameless until proven otherwise. */
+ cache->frameless_p = 1;
+}
+
+/* Allocate and initialize a frame cache. */