aboutsummaryrefslogtreecommitdiff
path: root/gdb/gdbserver/linux-aarch64-low.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/gdbserver/linux-aarch64-low.c')
-rw-r--r--gdb/gdbserver/linux-aarch64-low.c442
1 files changed, 299 insertions, 143 deletions
diff --git a/gdb/gdbserver/linux-aarch64-low.c b/gdb/gdbserver/linux-aarch64-low.c
index 506fb9e..b4181ed 100644
--- a/gdb/gdbserver/linux-aarch64-low.c
+++ b/gdb/gdbserver/linux-aarch64-low.c
@@ -1924,175 +1924,323 @@ can_encode_int32 (int32_t val, unsigned bits)
return rest == 0 || rest == -1;
}
-/* Relocate an instruction INSN from OLDLOC to TO and save the relocated
- instructions in BUF. The number of instructions in BUF is returned.
+/* Data passed to each method of aarch64_insn_visitor. */
- PC relative instructions need to be handled specifically:
+struct aarch64_insn_data
+{
+ /* The instruction address. */
+ CORE_ADDR insn_addr;
+};
- - B/BL
- - B.COND
- - CBZ/CBNZ
- - TBZ/TBNZ
- - ADR/ADRP
- - LDR/LDRSW (literal) */
+/* Visit different instructions by different methods. */
-static int
-aarch64_relocate_instruction (const CORE_ADDR to, const CORE_ADDR oldloc,
- uint32_t insn, uint32_t *buf)
+struct aarch64_insn_visitor
{
- uint32_t *p = buf;
+ /* Visit instruction B/BL OFFSET. */
+ void (*b) (const int is_bl, const int32_t offset,
+ struct aarch64_insn_data *data);
- int is_bl;
- int is64;
- int is_sw;
- int is_cbnz;
- int is_tbnz;
- int is_adrp;
- unsigned rn;
- unsigned rt;
- unsigned rd;
- unsigned cond;
- unsigned bit;
- int32_t offset;
+ /* Visit instruction B.COND OFFSET. */
+ void (*b_cond) (const unsigned cond, const int32_t offset,
+ struct aarch64_insn_data *data);
- if (aarch64_decode_b (oldloc, insn, &is_bl, &offset))
- {
- offset = (oldloc - to + offset);
+ /* Visit instruction CBZ/CBNZ Rn, OFFSET. */
+ void (*cb) (const int32_t offset, const int is_cbnz,
+ const unsigned rn, int is64,
+ struct aarch64_insn_data *data);
- if (can_encode_int32 (offset, 28))
- p += emit_b (p, is_bl, offset);
- else
- return 0;
- }
- else if (aarch64_decode_bcond (oldloc, insn, &cond, &offset))
- {
- offset = (oldloc - to + offset);
+ /* Visit instruction TBZ/TBNZ Rt, #BIT, OFFSET. */
+ void (*tb) (const int32_t offset, int is_tbnz,
+ const unsigned rt, unsigned bit,
+ struct aarch64_insn_data *data);
- if (can_encode_int32 (offset, 21))
- p += emit_bcond (p, cond, offset);
- else if (can_encode_int32 (offset, 28))
- {
- /* The offset is out of range for a conditional branch
- instruction but not for a unconditional branch. We can use
- the following instructions instead:
+ /* Visit instruction ADR/ADRP Rd, OFFSET. */
+ void (*adr) (const int32_t offset, const unsigned rd,
+ const int is_adrp, struct aarch64_insn_data *data);
+
+ /* Visit instruction LDR/LDRSW Rt, OFFSET. */
+ void (*ldr_literal) (const int32_t offset, const int is_sw,
+ const unsigned rt, const int is64,
+ struct aarch64_insn_data *data);
- B.COND TAKEN ; If cond is true, then jump to TAKEN.
- B NOT_TAKEN ; Else jump over TAKEN and continue.
- TAKEN:
- B #(offset - 8)
- NOT_TAKEN:
+ /* Visit instruction INSN of other kinds. */
+ void (*others) (const uint32_t insn, struct aarch64_insn_data *data);
+};
- */
+/* Sub-class of struct aarch64_insn_data, store information of
+ instruction relocation for fast tracepoint. Visitor can
+ relocate an instruction from BASE.INSN_ADDR to NEW_ADDR and save
+ the relocated instructions in buffer pointed by INSN_PTR. */
- p += emit_bcond (p, cond, 8);
- p += emit_b (p, 0, 8);
- p += emit_b (p, 0, offset - 8);
- }
- else
- return 0;
+struct aarch64_insn_relocation_data
+{
+ struct aarch64_insn_data base;
+
+ /* The new address the instruction is relocated to. */
+ CORE_ADDR new_addr;
+ /* Pointer to the buffer of relocated instruction(s). */
+ uint32_t *insn_ptr;
+};
+
+/* Implementation of aarch64_insn_visitor method "b". */
+
+static void
+aarch64_ftrace_insn_reloc_b (const int is_bl, const int32_t offset,
+ struct aarch64_insn_data *data)
+{
+ struct aarch64_insn_relocation_data *insn_reloc
+ = (struct aarch64_insn_relocation_data *) data;
+ int32_t new_offset
+ = insn_reloc->base.insn_addr - insn_reloc->new_addr + offset;
+
+ if (can_encode_int32 (new_offset, 28))
+ insn_reloc->insn_ptr += emit_b (insn_reloc->insn_ptr, is_bl, new_offset);
+}
+
+/* Implementation of aarch64_insn_visitor method "b_cond". */
+
+static void
+aarch64_ftrace_insn_reloc_b_cond (const unsigned cond, const int32_t offset,
+ struct aarch64_insn_data *data)
+{
+ struct aarch64_insn_relocation_data *insn_reloc
+ = (struct aarch64_insn_relocation_data *) data;
+ int32_t new_offset
+ = insn_reloc->base.insn_addr - insn_reloc->new_addr + offset;
+
+ if (can_encode_int32 (new_offset, 21))
+ {
+ insn_reloc->insn_ptr += emit_bcond (insn_reloc->insn_ptr, cond,
+ new_offset);
}
- else if (aarch64_decode_cb (oldloc, insn, &is64, &is_cbnz, &rn, &offset))
+ else if (can_encode_int32 (new_offset, 28))
{
- offset = (oldloc - to + offset);
+ /* The offset is out of range for a conditional branch
+ instruction but not for a unconditional branch. We can use
+ the following instructions instead:
- if (can_encode_int32 (offset, 21))
- p += emit_cb (p, is_cbnz, aarch64_register (rn, is64), offset);
- else if (can_encode_int32 (offset, 28))
- {
- /* The offset is out of range for a compare and branch
- instruction but not for a unconditional branch. We can use
- the following instructions instead:
-
- CBZ xn, TAKEN ; xn == 0, then jump to TAKEN.
- B NOT_TAKEN ; Else jump over TAKEN and continue.
- TAKEN:
- B #(offset - 8)
- NOT_TAKEN:
-
- */
- p += emit_cb (p, is_cbnz, aarch64_register (rn, is64), 8);
- p += emit_b (p, 0, 8);
- p += emit_b (p, 0, offset - 8);
- }
- else
- return 0;
+ B.COND TAKEN ; If cond is true, then jump to TAKEN.
+ B NOT_TAKEN ; Else jump over TAKEN and continue.
+ TAKEN:
+ B #(offset - 8)
+ NOT_TAKEN:
+
+ */
+
+ insn_reloc->insn_ptr += emit_bcond (insn_reloc->insn_ptr, cond, 8);
+ insn_reloc->insn_ptr += emit_b (insn_reloc->insn_ptr, 0, 8);
+ insn_reloc->insn_ptr += emit_b (insn_reloc->insn_ptr, 0, new_offset - 8);
}
- else if (aarch64_decode_tb (oldloc, insn, &is_tbnz, &bit, &rt, &offset))
- {
- offset = (oldloc - to + offset);
+}
- if (can_encode_int32 (offset, 16))
- p += emit_tb (p, is_tbnz, bit, aarch64_register (rt, 1), offset);
- else if (can_encode_int32 (offset, 28))
- {
- /* The offset is out of range for a test bit and branch
- instruction but not for a unconditional branch. We can use
- the following instructions instead:
-
- TBZ xn, #bit, TAKEN ; xn[bit] == 0, then jump to TAKEN.
- B NOT_TAKEN ; Else jump over TAKEN and continue.
- TAKEN:
- B #(offset - 8)
- NOT_TAKEN:
-
- */
- p += emit_tb (p, is_tbnz, bit, aarch64_register (rt, 1), 8);
- p += emit_b (p, 0, 8);
- p += emit_b (p, 0, offset - 8);
- }
- else
- return 0;
+/* Implementation of aarch64_insn_visitor method "cb". */
+
+static void
+aarch64_ftrace_insn_reloc_cb (const int32_t offset, const int is_cbnz,
+ const unsigned rn, int is64,
+ struct aarch64_insn_data *data)
+{
+ struct aarch64_insn_relocation_data *insn_reloc
+ = (struct aarch64_insn_relocation_data *) data;
+ int32_t new_offset
+ = insn_reloc->base.insn_addr - insn_reloc->new_addr + offset;
+
+ if (can_encode_int32 (new_offset, 21))
+ {
+ insn_reloc->insn_ptr += emit_cb (insn_reloc->insn_ptr, is_cbnz,
+ aarch64_register (rn, is64), new_offset);
}
- else if (aarch64_decode_adr (oldloc, insn, &is_adrp, &rd, &offset))
+ else if (can_encode_int32 (new_offset, 28))
{
+ /* The offset is out of range for a compare and branch
+ instruction but not for a unconditional branch. We can use
+ the following instructions instead:
+
+ CBZ xn, TAKEN ; xn == 0, then jump to TAKEN.
+ B NOT_TAKEN ; Else jump over TAKEN and continue.
+ TAKEN:
+ B #(offset - 8)
+ NOT_TAKEN:
+
+ */
+ insn_reloc->insn_ptr += emit_cb (insn_reloc->insn_ptr, is_cbnz,
+ aarch64_register (rn, is64), 8);
+ insn_reloc->insn_ptr += emit_b (insn_reloc->insn_ptr, 0, 8);
+ insn_reloc->insn_ptr += emit_b (insn_reloc->insn_ptr, 0, new_offset - 8);
+ }
+}
- /* We know exactly the address the ADR{P,} instruction will compute.
- We can just write it to the destination register. */
- CORE_ADDR address = oldloc + offset;
+/* Implementation of aarch64_insn_visitor method "tb". */
- if (is_adrp)
- {
- /* Clear the lower 12 bits of the offset to get the 4K page. */
- p += emit_mov_addr (p, aarch64_register (rd, 1),
- address & ~0xfff);
- }
- else
- p += emit_mov_addr (p, aarch64_register (rd, 1), address);
+static void
+aarch64_ftrace_insn_reloc_tb (const int32_t offset, int is_tbnz,
+ const unsigned rt, unsigned bit,
+ struct aarch64_insn_data *data)
+{
+ struct aarch64_insn_relocation_data *insn_reloc
+ = (struct aarch64_insn_relocation_data *) data;
+ int32_t new_offset
+ = insn_reloc->base.insn_addr - insn_reloc->new_addr + offset;
+
+ if (can_encode_int32 (new_offset, 16))
+ {
+ insn_reloc->insn_ptr += emit_tb (insn_reloc->insn_ptr, is_tbnz, bit,
+ aarch64_register (rt, 1), new_offset);
}
- else if (aarch64_decode_ldr_literal (oldloc, insn, &is_sw, &is64, &rt,
- &offset))
+ else if (can_encode_int32 (new_offset, 28))
{
- /* We know exactly what address to load from, and what register we
- can use:
+ /* The offset is out of range for a test bit and branch
+ instruction but not for a unconditional branch. We can use
+ the following instructions instead:
+
+ TBZ xn, #bit, TAKEN ; xn[bit] == 0, then jump to TAKEN.
+ B NOT_TAKEN ; Else jump over TAKEN and continue.
+ TAKEN:
+ B #(offset - 8)
+ NOT_TAKEN:
+
+ */
+ insn_reloc->insn_ptr += emit_tb (insn_reloc->insn_ptr, is_tbnz, bit,
+ aarch64_register (rt, 1), 8);
+ insn_reloc->insn_ptr += emit_b (insn_reloc->insn_ptr, 0, 8);
+ insn_reloc->insn_ptr += emit_b (insn_reloc->insn_ptr, 0,
+ new_offset - 8);
+ }
+}
- MOV xd, #(oldloc + offset)
- MOVK xd, #((oldloc + offset) >> 16), lsl #16
- ...
+/* Implementation of aarch64_insn_visitor method "adr". */
- LDR xd, [xd] ; or LDRSW xd, [xd]
+static void
+aarch64_ftrace_insn_reloc_adr (const int32_t offset, const unsigned rd,
+ const int is_adrp,
+ struct aarch64_insn_data *data)
+{
+ struct aarch64_insn_relocation_data *insn_reloc
+ = (struct aarch64_insn_relocation_data *) data;
+ /* We know exactly the address the ADR{P,} instruction will compute.
+ We can just write it to the destination register. */
+ CORE_ADDR address = data->insn_addr + offset;
- */
- CORE_ADDR address = oldloc + offset;
+ if (is_adrp)
+ {
+ /* Clear the lower 12 bits of the offset to get the 4K page. */
+ insn_reloc->insn_ptr += emit_mov_addr (insn_reloc->insn_ptr,
+ aarch64_register (rd, 1),
+ address & ~0xfff);
+ }
+ else
+ insn_reloc->insn_ptr += emit_mov_addr (insn_reloc->insn_ptr,
+ aarch64_register (rd, 1), address);
+}
- p += emit_mov_addr (p, aarch64_register (rt, 1), address);
+/* Implementation of aarch64_insn_visitor method "ldr_literal". */
- if (is_sw)
- p += emit_ldrsw (p, aarch64_register (rt, 1),
- aarch64_register (rt, 1),
- offset_memory_operand (0));
- else
- p += emit_ldr (p, aarch64_register (rt, is64),
- aarch64_register (rt, 1),
- offset_memory_operand (0));
- }
+static void
+aarch64_ftrace_insn_reloc_ldr_literal (const int32_t offset, const int is_sw,
+ const unsigned rt, const int is64,
+ struct aarch64_insn_data *data)
+{
+ struct aarch64_insn_relocation_data *insn_reloc
+ = (struct aarch64_insn_relocation_data *) data;
+ CORE_ADDR address = data->insn_addr + offset;
+
+ insn_reloc->insn_ptr += emit_mov_addr (insn_reloc->insn_ptr,
+ aarch64_register (rt, 1), address);
+
+ /* We know exactly what address to load from, and what register we
+ can use:
+
+ MOV xd, #(oldloc + offset)
+ MOVK xd, #((oldloc + offset) >> 16), lsl #16
+ ...
+
+ LDR xd, [xd] ; or LDRSW xd, [xd]
+
+ */
+
+ if (is_sw)
+ insn_reloc->insn_ptr += emit_ldrsw (insn_reloc->insn_ptr,
+ aarch64_register (rt, 1),
+ aarch64_register (rt, 1),
+ offset_memory_operand (0));
else
- {
- /* The instruction is not PC relative. Just re-emit it at the new
- location. */
- p += emit_insn (p, insn);
- }
+ insn_reloc->insn_ptr += emit_ldr (insn_reloc->insn_ptr,
+ aarch64_register (rt, is64),
+ aarch64_register (rt, 1),
+ offset_memory_operand (0));
+}
+
+/* Implementation of aarch64_insn_visitor method "others". */
+
+static void
+aarch64_ftrace_insn_reloc_others (const uint32_t insn,
+ struct aarch64_insn_data *data)
+{
+ struct aarch64_insn_relocation_data *insn_reloc
+ = (struct aarch64_insn_relocation_data *) data;
- return (int) (p - buf);
+ /* The instruction is not PC relative. Just re-emit it at the new
+ location. */
+ insn_reloc->insn_ptr += emit_insn (insn_reloc->insn_ptr, insn);
+}
+
+static const struct aarch64_insn_visitor visitor =
+{
+ aarch64_ftrace_insn_reloc_b,
+ aarch64_ftrace_insn_reloc_b_cond,
+ aarch64_ftrace_insn_reloc_cb,
+ aarch64_ftrace_insn_reloc_tb,
+ aarch64_ftrace_insn_reloc_adr,
+ aarch64_ftrace_insn_reloc_ldr_literal,
+ aarch64_ftrace_insn_reloc_others,
+};
+
+/* Visit an instruction INSN by VISITOR with all needed information in DATA.
+
+ PC relative instructions need to be handled specifically:
+
+ - B/BL
+ - B.COND
+ - CBZ/CBNZ
+ - TBZ/TBNZ
+ - ADR/ADRP
+ - LDR/LDRSW (literal) */
+
+static void
+aarch64_relocate_instruction (uint32_t insn,
+ const struct aarch64_insn_visitor *visitor,
+ struct aarch64_insn_data *data)
+{
+ int is_bl;
+ int is64;
+ int is_sw;
+ int is_cbnz;
+ int is_tbnz;
+ int is_adrp;
+ unsigned rn;
+ unsigned rt;
+ unsigned rd;
+ unsigned cond;
+ unsigned bit;
+ int32_t offset;
+
+ if (aarch64_decode_b (data->insn_addr, insn, &is_bl, &offset))
+ visitor->b (is_bl, offset, data);
+ else if (aarch64_decode_bcond (data->insn_addr, insn, &cond, &offset))
+ visitor->b_cond (cond, offset, data);
+ else if (aarch64_decode_cb (data->insn_addr, insn, &is64, &is_cbnz, &rn,
+ &offset))
+ visitor->cb (offset, is_cbnz, rn, is64, data);
+ else if (aarch64_decode_tb (data->insn_addr, insn, &is_tbnz, &bit, &rt,
+ &offset))
+ visitor->tb (offset, is_tbnz, rt, bit, data);
+ else if (aarch64_decode_adr (data->insn_addr, insn, &is_adrp, &rd, &offset))
+ visitor->adr (offset, rd, is_adrp, data);
+ else if (aarch64_decode_ldr_literal (data->insn_addr, insn, &is_sw, &is64,
+ &rt, &offset))
+ visitor->ldr_literal (offset, is_sw, rt, is64, data);
+ else
+ visitor->others (insn, data);
}
/* Implementation of linux_target_ops method
@@ -2119,6 +2267,7 @@ aarch64_install_fast_tracepoint_jump_pad (CORE_ADDR tpoint,
int i;
uint32_t insn;
CORE_ADDR buildaddr = *jump_entry;
+ struct aarch64_insn_relocation_data insn_data;
/* We need to save the current state on the stack both to restore it
later and to collect register values when the tracepoint is hit.
@@ -2421,9 +2570,16 @@ aarch64_install_fast_tracepoint_jump_pad (CORE_ADDR tpoint,
/* Now emit the relocated instruction. */
*adjusted_insn_addr = buildaddr;
target_read_uint32 (tpaddr, &insn);
- i = aarch64_relocate_instruction (buildaddr, tpaddr, insn, buf);
+
+ insn_data.base.insn_addr = tpaddr;
+ insn_data.new_addr = buildaddr;
+ insn_data.insn_ptr = buf;
+
+ aarch64_relocate_instruction (insn, &visitor,
+ (struct aarch64_insn_data *) &insn_data);
+
/* We may not have been able to relocate the instruction. */
- if (i == 0)
+ if (insn_data.insn_ptr == buf)
{
sprintf (err,
"E.Could not relocate instruction from %s to %s.",
@@ -2432,7 +2588,7 @@ aarch64_install_fast_tracepoint_jump_pad (CORE_ADDR tpoint,
return 1;
}
else
- append_insns (&buildaddr, i, buf);
+ append_insns (&buildaddr, insn_data.insn_ptr - buf, buf);
*adjusted_insn_addr_end = buildaddr;
/* Go back to the start of the buffer. */