From afbe19f83ad1af46e7b1f1639d9fa0eb873dcbb5 Mon Sep 17 00:00:00 2001 From: Pierre Langlois Date: Mon, 21 Sep 2015 15:01:04 +0100 Subject: [PATCH] Implement target_emit_ops This patch implements compiling agent expressions to native code for AArch64. This allows us to compile conditions set on fast tracepoints. The compiled function has the following prologue: High *------------------------------------------------------* | LR | | FP | <- FP | x1 (ULONGEST *value) | | x0 (unsigned char *regs) | Low *------------------------------------------------------* We save the function's argument on the stack as well as the return address and the frame pointer. We then set the current frame pointer to point to the previous one. The generated code for the expression will freely update the stack pointer so we use the frame pointer to refer to `*value' and `*regs'. `*value' needs to be accessed in the epilogue of the function, in order to set it to whatever is on top of the stack. `*regs' needs to be passed down to the `gdb_agent_get_raw_reg' function with the `reg' operation. gdb/gdbserver/ChangeLog: * linux-aarch64-low-.c: Include ax.h and tracepoint.h. (enum aarch64_opcodes) , , , , , , , , , , , , , : New. (enum aarch64_condition_codes): New enum. (w0): New static global. (fp): Likewise. (lr): Likewise. (struct aarch64_memory_operand) : New MEMORY_OPERAND_POSTINDEX type. (postindex_memory_operand): New helper function. (emit_ret): New function. (emit_load_store_pair): New function, factored out of emit_stp with support for MEMORY_OPERAND_POSTINDEX. (emit_stp): Rewrite using emit_load_store_pair. (emit_ldp): New function. (emit_load_store): Likewise. (emit_ldr): Mention post-index instruction in comment. (emit_ldrh): New function. (emit_ldrb): New function. (emit_ldrsw): Mention post-index instruction in comment. (emit_str): Likewise. (emit_subs): New function. (emit_cmp): Likewise. (emit_and): Likewise. (emit_orr): Likewise. (emit_orn): Likewise. (emit_eor): Likewise. (emit_mvn): Likewise. (emit_lslv): Likewise. (emit_lsrv): Likewise. (emit_asrv): Likewise. (emit_mul): Likewise. (emit_sbfm): Likewise. (emit_sbfx): Likewise. (emit_ubfm): Likewise. (emit_ubfx): Likewise. (emit_csinc): Likewise. (emit_cset): Likewise. (emit_nop): Likewise. (emit_ops_insns): New helper function. (emit_pop): Likewise. (emit_push): Likewise. (aarch64_emit_prologue): New function. (aarch64_emit_epilogue): Likewise. (aarch64_emit_add): Likewise. (aarch64_emit_sub): Likewise. (aarch64_emit_mul): Likewise. (aarch64_emit_lsh): Likewise. (aarch64_emit_rsh_signed): Likewise. (aarch64_emit_rsh_unsigned): Likewise. (aarch64_emit_ext): Likewise. (aarch64_emit_log_not): Likewise. (aarch64_emit_bit_and): Likewise. (aarch64_emit_bit_or): Likewise. (aarch64_emit_bit_xor): Likewise. (aarch64_emit_bit_not): Likewise. (aarch64_emit_equal): Likewise. (aarch64_emit_less_signed): Likewise. (aarch64_emit_less_unsigned): Likewise. (aarch64_emit_ref): Likewise. (aarch64_emit_if_goto): Likewise. (aarch64_emit_goto): Likewise. (aarch64_write_goto_address): Likewise. (aarch64_emit_const): Likewise. (aarch64_emit_call): Likewise. (aarch64_emit_reg): Likewise. (aarch64_emit_pop): Likewise. (aarch64_emit_stack_flush): Likewise. (aarch64_emit_zero_ext): Likewise. (aarch64_emit_swap): Likewise. (aarch64_emit_stack_adjust): Likewise. (aarch64_emit_int_call_1): Likewise. (aarch64_emit_void_call_2): Likewise. (aarch64_emit_eq_goto): Likewise. (aarch64_emit_ne_goto): Likewise. (aarch64_emit_lt_goto): Likewise. (aarch64_emit_le_goto): Likewise. (aarch64_emit_gt_goto): Likewise. (aarch64_emit_ge_got): Likewise. (aarch64_emit_ops_impl): New static global variable. (aarch64_emit_ops): New target function, return &aarch64_emit_ops_impl. (struct linux_target_ops): Install it. --- gdb/gdbserver/ChangeLog | 87 ++ gdb/gdbserver/linux-aarch64-low.c | 1227 ++++++++++++++++++++++++++++- 2 files changed, 1300 insertions(+), 14 deletions(-) diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index d9ad6bb3567..d09c454b022 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,90 @@ +2015-09-21 Pierre Langlois + + * linux-aarch64-low-.c: Include ax.h and tracepoint.h. + (enum aarch64_opcodes) , , , , , + , , , , , , , , + : New. + (enum aarch64_condition_codes): New enum. + (w0): New static global. + (fp): Likewise. + (lr): Likewise. + (struct aarch64_memory_operand) : New + MEMORY_OPERAND_POSTINDEX type. + (postindex_memory_operand): New helper function. + (emit_ret): New function. + (emit_load_store_pair): New function, factored out of emit_stp + with support for MEMORY_OPERAND_POSTINDEX. + (emit_stp): Rewrite using emit_load_store_pair. + (emit_ldp): New function. + (emit_load_store): Likewise. + (emit_ldr): Mention post-index instruction in comment. + (emit_ldrh): New function. + (emit_ldrb): New function. + (emit_ldrsw): Mention post-index instruction in comment. + (emit_str): Likewise. + (emit_subs): New function. + (emit_cmp): Likewise. + (emit_and): Likewise. + (emit_orr): Likewise. + (emit_orn): Likewise. + (emit_eor): Likewise. + (emit_mvn): Likewise. + (emit_lslv): Likewise. + (emit_lsrv): Likewise. + (emit_asrv): Likewise. + (emit_mul): Likewise. + (emit_sbfm): Likewise. + (emit_sbfx): Likewise. + (emit_ubfm): Likewise. + (emit_ubfx): Likewise. + (emit_csinc): Likewise. + (emit_cset): Likewise. + (emit_nop): Likewise. + (emit_ops_insns): New helper function. + (emit_pop): Likewise. + (emit_push): Likewise. + (aarch64_emit_prologue): New function. + (aarch64_emit_epilogue): Likewise. + (aarch64_emit_add): Likewise. + (aarch64_emit_sub): Likewise. + (aarch64_emit_mul): Likewise. + (aarch64_emit_lsh): Likewise. + (aarch64_emit_rsh_signed): Likewise. + (aarch64_emit_rsh_unsigned): Likewise. + (aarch64_emit_ext): Likewise. + (aarch64_emit_log_not): Likewise. + (aarch64_emit_bit_and): Likewise. + (aarch64_emit_bit_or): Likewise. + (aarch64_emit_bit_xor): Likewise. + (aarch64_emit_bit_not): Likewise. + (aarch64_emit_equal): Likewise. + (aarch64_emit_less_signed): Likewise. + (aarch64_emit_less_unsigned): Likewise. + (aarch64_emit_ref): Likewise. + (aarch64_emit_if_goto): Likewise. + (aarch64_emit_goto): Likewise. + (aarch64_write_goto_address): Likewise. + (aarch64_emit_const): Likewise. + (aarch64_emit_call): Likewise. + (aarch64_emit_reg): Likewise. + (aarch64_emit_pop): Likewise. + (aarch64_emit_stack_flush): Likewise. + (aarch64_emit_zero_ext): Likewise. + (aarch64_emit_swap): Likewise. + (aarch64_emit_stack_adjust): Likewise. + (aarch64_emit_int_call_1): Likewise. + (aarch64_emit_void_call_2): Likewise. + (aarch64_emit_eq_goto): Likewise. + (aarch64_emit_ne_goto): Likewise. + (aarch64_emit_lt_goto): Likewise. + (aarch64_emit_le_goto): Likewise. + (aarch64_emit_gt_goto): Likewise. + (aarch64_emit_ge_got): Likewise. + (aarch64_emit_ops_impl): New static global variable. + (aarch64_emit_ops): New target function, return + &aarch64_emit_ops_impl. + (struct linux_target_ops): Install it. + 2015-09-21 Pierre Langlois * Makefile.in (linux-aarch64-ipa.o, aarch64-ipa.o): New rules. diff --git a/gdb/gdbserver/linux-aarch64-low.c b/gdb/gdbserver/linux-aarch64-low.c index 8e007052e95..bc9a5c703ef 100644 --- a/gdb/gdbserver/linux-aarch64-low.c +++ b/gdb/gdbserver/linux-aarch64-low.c @@ -26,6 +26,8 @@ #include "arch/aarch64-insn.h" #include "linux-aarch32-low.h" #include "elf/common.h" +#include "ax.h" +#include "tracepoint.h" #include #include @@ -667,6 +669,8 @@ enum aarch64_opcodes TBNZ = 0x37000000 | B, /* BLR 1101 0110 0011 1111 0000 00rr rrr0 0000 */ BLR = 0xd63f0000, + /* RET 1101 0110 0101 1111 0000 00rr rrr0 0000 */ + RET = 0xd65f0000, /* STP s010 100o o0ii iiii irrr rrrr rrrr rrrr */ /* LDP s010 100o o1ii iiii irrr rrrr rrrr rrrr */ /* STP (SIMD&VFP) ss10 110o o0ii iiii irrr rrrr rrrr rrrr */ @@ -696,6 +700,29 @@ enum aarch64_opcodes /* SUBS s11o ooo1 xxxx xxxx xxxx xxxx xxxx xxxx */ ADD = 0x01000000, SUB = 0x40000000 | ADD, + SUBS = 0x20000000 | SUB, + /* AND s000 1010 xx0x xxxx xxxx xxxx xxxx xxxx */ + /* ORR s010 1010 xx0x xxxx xxxx xxxx xxxx xxxx */ + /* ORN s010 1010 xx1x xxxx xxxx xxxx xxxx xxxx */ + /* EOR s100 1010 xx0x xxxx xxxx xxxx xxxx xxxx */ + AND = 0x0a000000, + ORR = 0x20000000 | AND, + ORN = 0x00200000 | ORR, + EOR = 0x40000000 | AND, + /* LSLV s001 1010 110r rrrr 0010 00rr rrrr rrrr */ + /* LSRV s001 1010 110r rrrr 0010 01rr rrrr rrrr */ + /* ASRV s001 1010 110r rrrr 0010 10rr rrrr rrrr */ + LSLV = 0x1ac02000, + LSRV = 0x00000400 | LSLV, + ASRV = 0x00000800 | LSLV, + /* SBFM s001 0011 0nii iiii iiii iirr rrrr rrrr */ + SBFM = 0x13000000, + /* UBFM s101 0011 0nii iiii iiii iirr rrrr rrrr */ + UBFM = 0x40000000 | SBFM, + /* CSINC s001 1010 100r rrrr cccc 01rr rrrr rrrr */ + CSINC = 0x9a800400, + /* MUL s001 1011 000r rrrr 0111 11rr rrrr rrrr */ + MUL = 0x1b007c00, /* MSR (register) 1101 0101 0001 oooo oooo oooo ooor rrrr */ /* MRS 1101 0101 0011 oooo oooo oooo ooor rrrr */ MSR = 0xd5100000, @@ -704,6 +731,20 @@ enum aarch64_opcodes HINT = 0xd503201f, SEVL = (5 << 5) | HINT, WFE = (2 << 5) | HINT, + NOP = (0 << 5) | HINT, +}; + +/* List of condition codes that we need. */ + +enum aarch64_condition_codes +{ + EQ = 0x0, + NE = 0x1, + LO = 0x3, + GE = 0xa, + LT = 0xb, + GT = 0xc, + LE = 0xd, }; /* Representation of a general purpose register of the form xN or wN. @@ -746,12 +787,15 @@ static const struct aarch64_register x3 = { 3, 1 }; static const struct aarch64_register x4 = { 4, 1 }; /* General purpose scratch registers (32 bit). */ +static const struct aarch64_register w0 = { 0, 0 }; static const struct aarch64_register w2 = { 2, 0 }; /* Intra-procedure scratch registers. */ static const struct aarch64_register ip0 = { 16, 1 }; /* Special purpose registers. */ +static const struct aarch64_register fp = { 29, 1 }; +static const struct aarch64_register lr = { 30, 1 }; static const struct aarch64_register sp = { 31, 1 }; static const struct aarch64_register xzr = { 31, 1 }; @@ -804,8 +848,9 @@ immediate_operand (uint32_t imm) The types correspond to the following variants: - MEMORY_OPERAND_OFFSET: LDR rt, [rn, #offset] - MEMORY_OPERAND_PREINDEX: LDR rt, [rn, #index]! */ + MEMORY_OPERAND_OFFSET: LDR rt, [rn, #offset] + MEMORY_OPERAND_PREINDEX: LDR rt, [rn, #index]! + MEMORY_OPERAND_POSTINDEX: LDR rt, [rn], #index */ struct aarch64_memory_operand { @@ -814,6 +859,7 @@ struct aarch64_memory_operand { MEMORY_OPERAND_OFFSET, MEMORY_OPERAND_PREINDEX, + MEMORY_OPERAND_POSTINDEX, } type; /* Index from the base register. */ int32_t index; @@ -841,6 +887,17 @@ preindex_memory_operand (int32_t index) return (struct aarch64_memory_operand) { MEMORY_OPERAND_PREINDEX, index }; } +/* Helper function to create a post-index memory operand. + + For example: + p += emit_ldr (p, x0, sp, postindex_memory_operand (16)); */ + +static struct aarch64_memory_operand +postindex_memory_operand (int32_t index) +{ + return (struct aarch64_memory_operand) { MEMORY_OPERAND_POSTINDEX, index }; +} + /* System control registers. These special registers can be written and read with the MRS and MSR instructions. @@ -973,20 +1030,24 @@ emit_blr (uint32_t *buf, struct aarch64_register rn) return emit_insn (buf, BLR | ENCODE (rn.num, 5, 5)); } -/* Write a STP instruction into *BUF. +/* Write a RET instruction into *BUF. - STP rt, rt2, [rn, #offset] - STP rt, rt2, [rn, #index]! + RET xn - RT and RT2 are the registers to store. - RN is the base address register. - OFFSET is the immediate to add to the base address. It is limited to a - -512 .. 504 range (7 bits << 3). */ + RN is the register to branch to. */ static int -emit_stp (uint32_t *buf, struct aarch64_register rt, - struct aarch64_register rt2, struct aarch64_register rn, - struct aarch64_memory_operand operand) +emit_ret (uint32_t *buf, struct aarch64_register rn) +{ + return emit_insn (buf, RET | ENCODE (rn.num, 5, 5)); +} + +static int +emit_load_store_pair (uint32_t *buf, enum aarch64_opcodes opcode, + struct aarch64_register rt, + struct aarch64_register rt2, + struct aarch64_register rn, + struct aarch64_memory_operand operand) { uint32_t opc; uint32_t pre_index; @@ -1005,6 +1066,12 @@ emit_stp (uint32_t *buf, struct aarch64_register rt, write_back = ENCODE (0, 1, 23); break; } + case MEMORY_OPERAND_POSTINDEX: + { + pre_index = ENCODE (0, 1, 24); + write_back = ENCODE (1, 1, 23); + break; + } case MEMORY_OPERAND_PREINDEX: { pre_index = ENCODE (1, 1, 24); @@ -1015,11 +1082,49 @@ emit_stp (uint32_t *buf, struct aarch64_register rt, return 0; } - return emit_insn (buf, STP | opc | pre_index | write_back + return emit_insn (buf, opcode | opc | pre_index | write_back | ENCODE (operand.index >> 3, 7, 15) | ENCODE (rt2.num, 5, 10) | ENCODE (rn.num, 5, 5) | ENCODE (rt.num, 5, 0)); } +/* Write a STP instruction into *BUF. + + STP rt, rt2, [rn, #offset] + STP rt, rt2, [rn, #index]! + STP rt, rt2, [rn], #index + + RT and RT2 are the registers to store. + RN is the base address register. + OFFSET is the immediate to add to the base address. It is limited to a + -512 .. 504 range (7 bits << 3). */ + +static int +emit_stp (uint32_t *buf, struct aarch64_register rt, + struct aarch64_register rt2, struct aarch64_register rn, + struct aarch64_memory_operand operand) +{ + return emit_load_store_pair (buf, STP, rt, rt2, rn, operand); +} + +/* Write a LDP instruction into *BUF. + + LDP rt, rt2, [rn, #offset] + LDP rt, rt2, [rn, #index]! + LDP rt, rt2, [rn], #index + + RT and RT2 are the registers to store. + RN is the base address register. + OFFSET is the immediate to add to the base address. It is limited to a + -512 .. 504 range (7 bits << 3). */ + +static int +emit_ldp (uint32_t *buf, struct aarch64_register rt, + struct aarch64_register rt2, struct aarch64_register rn, + struct aarch64_memory_operand operand) +{ + return emit_load_store_pair (buf, LDP, rt, rt2, rn, operand); +} + /* Write a LDP (SIMD&VFP) instruction using Q registers into *BUF. LDP qt, qt2, [rn, #offset] @@ -1081,6 +1186,16 @@ emit_load_store (uint32_t *buf, uint32_t size, enum aarch64_opcodes opcode, | ENCODE (operand.index >> 3, 12, 10) | ENCODE (rn.num, 5, 5) | ENCODE (rt.num, 5, 0)); } + case MEMORY_OPERAND_POSTINDEX: + { + uint32_t post_index = ENCODE (1, 2, 10); + + op = ENCODE (0, 1, 24); + + return emit_insn (buf, opcode | ENCODE (size, 2, 30) | op + | post_index | ENCODE (operand.index, 9, 12) + | ENCODE (rn.num, 5, 5) | ENCODE (rt.num, 5, 0)); + } case MEMORY_OPERAND_PREINDEX: { uint32_t pre_index = ENCODE (3, 2, 10); @@ -1100,6 +1215,7 @@ emit_load_store (uint32_t *buf, uint32_t size, enum aarch64_opcodes opcode, LDR rt, [rn, #offset] LDR rt, [rn, #index]! + LDR rt, [rn], #index RT is the register to store. RN is the base address register. @@ -1113,10 +1229,49 @@ emit_ldr (uint32_t *buf, struct aarch64_register rt, return emit_load_store (buf, rt.is64 ? 3 : 2, LDR, rt, rn, operand); } +/* Write a LDRH instruction into *BUF. + + LDRH wt, [xn, #offset] + LDRH wt, [xn, #index]! + LDRH wt, [xn], #index + + RT is the register to store. + RN is the base address register. + OFFSET is the immediate to add to the base address. It is limited to + 0 .. 32760 range (12 bits << 3). */ + +static int +emit_ldrh (uint32_t *buf, struct aarch64_register rt, + struct aarch64_register rn, + struct aarch64_memory_operand operand) +{ + return emit_load_store (buf, 1, LDR, rt, rn, operand); +} + +/* Write a LDRB instruction into *BUF. + + LDRB wt, [xn, #offset] + LDRB wt, [xn, #index]! + LDRB wt, [xn], #index + + RT is the register to store. + RN is the base address register. + OFFSET is the immediate to add to the base address. It is limited to + 0 .. 32760 range (12 bits << 3). */ + +static int +emit_ldrb (uint32_t *buf, struct aarch64_register rt, + struct aarch64_register rn, + struct aarch64_memory_operand operand) +{ + return emit_load_store (buf, 0, LDR, rt, rn, operand); +} + /* Write a LDRSW instruction into *BUF. The register size is 64-bit. LDRSW xt, [rn, #offset] LDRSW xt, [rn, #index]! + LDRSW xt, [rn], #index RT is the register to store. RN is the base address register. @@ -1135,6 +1290,7 @@ emit_ldrsw (uint32_t *buf, struct aarch64_register rt, STR rt, [rn, #offset] STR rt, [rn, #index]! + STR rt, [rn], #index RT is the register to store. RN is the base address register. @@ -1377,6 +1533,165 @@ emit_mov_addr (uint32_t *buf, struct aarch64_register rd, CORE_ADDR addr) return p - buf; } +/* Write a SUBS instruction into *BUF. + + SUBS rd, rn, rm + + This instruction update the condition flags. + + RD is the destination register. + RN and RM are the source registers. */ + +static int +emit_subs (uint32_t *buf, struct aarch64_register rd, + struct aarch64_register rn, struct aarch64_operand operand) +{ + return emit_data_processing (buf, SUBS, rd, rn, operand); +} + +/* Write a CMP instruction into *BUF. + + CMP rn, rm + + This instruction is an alias of SUBS xzr, rn, rm. + + RN and RM are the registers to compare. */ + +static int +emit_cmp (uint32_t *buf, struct aarch64_register rn, + struct aarch64_operand operand) +{ + return emit_subs (buf, xzr, rn, operand); +} + +/* Write a AND instruction into *BUF. + + AND rd, rn, rm + + RD is the destination register. + RN and RM are the source registers. */ + +static int +emit_and (uint32_t *buf, struct aarch64_register rd, + struct aarch64_register rn, struct aarch64_register rm) +{ + return emit_data_processing_reg (buf, AND, rd, rn, rm); +} + +/* Write a ORR instruction into *BUF. + + ORR rd, rn, rm + + RD is the destination register. + RN and RM are the source registers. */ + +static int +emit_orr (uint32_t *buf, struct aarch64_register rd, + struct aarch64_register rn, struct aarch64_register rm) +{ + return emit_data_processing_reg (buf, ORR, rd, rn, rm); +} + +/* Write a ORN instruction into *BUF. + + ORN rd, rn, rm + + RD is the destination register. + RN and RM are the source registers. */ + +static int +emit_orn (uint32_t *buf, struct aarch64_register rd, + struct aarch64_register rn, struct aarch64_register rm) +{ + return emit_data_processing_reg (buf, ORN, rd, rn, rm); +} + +/* Write a EOR instruction into *BUF. + + EOR rd, rn, rm + + RD is the destination register. + RN and RM are the source registers. */ + +static int +emit_eor (uint32_t *buf, struct aarch64_register rd, + struct aarch64_register rn, struct aarch64_register rm) +{ + return emit_data_processing_reg (buf, EOR, rd, rn, rm); +} + +/* Write a MVN instruction into *BUF. + + MVN rd, rm + + This is an alias for ORN rd, xzr, rm. + + RD is the destination register. + RM is the source register. */ + +static int +emit_mvn (uint32_t *buf, struct aarch64_register rd, + struct aarch64_register rm) +{ + return emit_orn (buf, rd, xzr, rm); +} + +/* Write a LSLV instruction into *BUF. + + LSLV rd, rn, rm + + RD is the destination register. + RN and RM are the source registers. */ + +static int +emit_lslv (uint32_t *buf, struct aarch64_register rd, + struct aarch64_register rn, struct aarch64_register rm) +{ + return emit_data_processing_reg (buf, LSLV, rd, rn, rm); +} + +/* Write a LSRV instruction into *BUF. + + LSRV rd, rn, rm + + RD is the destination register. + RN and RM are the source registers. */ + +static int +emit_lsrv (uint32_t *buf, struct aarch64_register rd, + struct aarch64_register rn, struct aarch64_register rm) +{ + return emit_data_processing_reg (buf, LSRV, rd, rn, rm); +} + +/* Write a ASRV instruction into *BUF. + + ASRV rd, rn, rm + + RD is the destination register. + RN and RM are the source registers. */ + +static int +emit_asrv (uint32_t *buf, struct aarch64_register rd, + struct aarch64_register rn, struct aarch64_register rm) +{ + return emit_data_processing_reg (buf, ASRV, rd, rn, rm); +} + +/* Write a MUL instruction into *BUF. + + MUL rd, rn, rm + + RD is the destination register. + RN and RM are the source registers. */ + +static int +emit_mul (uint32_t *buf, struct aarch64_register rd, + struct aarch64_register rn, struct aarch64_register rm) +{ + return emit_data_processing_reg (buf, MUL, rd, rn, rm); +} + /* Write a MRS instruction into *BUF. The register size is 64-bit. MRS xt, system_reg @@ -1427,6 +1742,150 @@ emit_wfe (uint32_t *buf) return emit_insn (buf, WFE); } +/* Write a SBFM instruction into *BUF. + + SBFM rd, rn, #immr, #imms + + This instruction moves the bits from #immr to #imms into the + destination, sign extending the result. + + RD is the destination register. + RN is the source register. + IMMR is the bit number to start at (least significant bit). + IMMS is the bit number to stop at (most significant bit). */ + +static int +emit_sbfm (uint32_t *buf, struct aarch64_register rd, + struct aarch64_register rn, uint32_t immr, uint32_t imms) +{ + uint32_t size = ENCODE (rd.is64, 1, 31); + uint32_t n = ENCODE (rd.is64, 1, 22); + + return emit_insn (buf, SBFM | size | n | ENCODE (immr, 6, 16) + | ENCODE (imms, 6, 10) | ENCODE (rn.num, 5, 5) + | ENCODE (rd.num, 5, 0)); +} + +/* Write a SBFX instruction into *BUF. + + SBFX rd, rn, #lsb, #width + + This instruction moves #width bits from #lsb into the destination, sign + extending the result. This is an alias for: + + SBFM rd, rn, #lsb, #(lsb + width - 1) + + RD is the destination register. + RN is the source register. + LSB is the bit number to start at (least significant bit). + WIDTH is the number of bits to move. */ + +static int +emit_sbfx (uint32_t *buf, struct aarch64_register rd, + struct aarch64_register rn, uint32_t lsb, uint32_t width) +{ + return emit_sbfm (buf, rd, rn, lsb, lsb + width - 1); +} + +/* Write a UBFM instruction into *BUF. + + UBFM rd, rn, #immr, #imms + + This instruction moves the bits from #immr to #imms into the + destination, extending the result with zeros. + + RD is the destination register. + RN is the source register. + IMMR is the bit number to start at (least significant bit). + IMMS is the bit number to stop at (most significant bit). */ + +static int +emit_ubfm (uint32_t *buf, struct aarch64_register rd, + struct aarch64_register rn, uint32_t immr, uint32_t imms) +{ + uint32_t size = ENCODE (rd.is64, 1, 31); + uint32_t n = ENCODE (rd.is64, 1, 22); + + return emit_insn (buf, UBFM | size | n | ENCODE (immr, 6, 16) + | ENCODE (imms, 6, 10) | ENCODE (rn.num, 5, 5) + | ENCODE (rd.num, 5, 0)); +} + +/* Write a UBFX instruction into *BUF. + + UBFX rd, rn, #lsb, #width + + This instruction moves #width bits from #lsb into the destination, + extending the result with zeros. This is an alias for: + + UBFM rd, rn, #lsb, #(lsb + width - 1) + + RD is the destination register. + RN is the source register. + LSB is the bit number to start at (least significant bit). + WIDTH is the number of bits to move. */ + +static int +emit_ubfx (uint32_t *buf, struct aarch64_register rd, + struct aarch64_register rn, uint32_t lsb, uint32_t width) +{ + return emit_ubfm (buf, rd, rn, lsb, lsb + width - 1); +} + +/* Write a CSINC instruction into *BUF. + + CSINC rd, rn, rm, cond + + This instruction conditionally increments rn or rm and places the result + in rd. rn is chosen is the condition is true. + + RD is the destination register. + RN and RM are the source registers. + COND is the encoded condition. */ + +static int +emit_csinc (uint32_t *buf, struct aarch64_register rd, + struct aarch64_register rn, struct aarch64_register rm, + unsigned cond) +{ + uint32_t size = ENCODE (rd.is64, 1, 31); + + return emit_insn (buf, CSINC | size | ENCODE (rm.num, 5, 16) + | ENCODE (cond, 4, 12) | ENCODE (rn.num, 5, 5) + | ENCODE (rd.num, 5, 0)); +} + +/* Write a CSET instruction into *BUF. + + CSET rd, cond + + This instruction conditionally write 1 or 0 in the destination register. + 1 is written if the condition is true. This is an alias for: + + CSINC rd, xzr, xzr, !cond + + Note that the condition needs to be inverted. + + RD is the destination register. + RN and RM are the source registers. + COND is the encoded condition. */ + +static int +emit_cset (uint32_t *buf, struct aarch64_register rd, unsigned cond) +{ + /* The least significant bit of the condition needs toggling in order to + invert it. */ + return emit_csinc (buf, rd, xzr, xzr, cond ^ 0x1); +} + +/* Write a NOP instruction into *BUF. */ + +static int +emit_nop (uint32_t *buf) +{ + return emit_insn (buf, NOP); +} + /* Write LEN instructions from BUF into the inferior memory at *TO. Note instructions are always little endian on AArch64, unlike data. */ @@ -2011,6 +2470,746 @@ aarch64_install_fast_tracepoint_jump_pad (CORE_ADDR tpoint, return 0; } +/* Helper function writing LEN instructions from START into + current_insn_ptr. */ + +static void +emit_ops_insns (const uint32_t *start, int len) +{ + CORE_ADDR buildaddr = current_insn_ptr; + + if (debug_threads) + debug_printf ("Adding %d instrucions at %s\n", + len, paddress (buildaddr)); + + append_insns (&buildaddr, len, start); + current_insn_ptr = buildaddr; +} + +/* Pop a register from the stack. */ + +static int +emit_pop (uint32_t *buf, struct aarch64_register rt) +{ + return emit_ldr (buf, rt, sp, postindex_memory_operand (1 * 16)); +} + +/* Push a register on the stack. */ + +static int +emit_push (uint32_t *buf, struct aarch64_register rt) +{ + return emit_str (buf, rt, sp, preindex_memory_operand (-1 * 16)); +} + +/* Implementation of emit_ops method "emit_prologue". */ + +static void +aarch64_emit_prologue (void) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + /* This function emit a prologue for the following function prototype: + + enum eval_result_type f (unsigned char *regs, + ULONGEST *value); + + The first argument is a buffer of raw registers. The second + argument is the result of + evaluating the expression, which will be set to whatever is on top of + the stack at the end. + + The stack set up by the prologue is as such: + + High *------------------------------------------------------* + | LR | + | FP | <- FP + | x1 (ULONGEST *value) | + | x0 (unsigned char *regs) | + Low *------------------------------------------------------* + + As we are implementing a stack machine, each opcode can expand the + stack so we never know how far we are from the data saved by this + prologue. In order to be able refer to value and regs later, we save + the current stack pointer in the frame pointer. This way, it is not + clobbered when calling C functions. + + Finally, throughtout every operation, we are using register x0 as the + top of the stack, and x1 as a scratch register. */ + + p += emit_stp (p, x0, x1, sp, preindex_memory_operand (-2 * 16)); + p += emit_str (p, lr, sp, offset_memory_operand (3 * 8)); + p += emit_str (p, fp, sp, offset_memory_operand (2 * 8)); + + p += emit_add (p, fp, sp, immediate_operand (2 * 8)); + + + emit_ops_insns (buf, p - buf); +} + +/* Implementation of emit_ops method "emit_epilogue". */ + +static void +aarch64_emit_epilogue (void) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + /* Store the result of the expression (x0) in *value. */ + p += emit_sub (p, x1, fp, immediate_operand (1 * 8)); + p += emit_ldr (p, x1, x1, offset_memory_operand (0)); + p += emit_str (p, x0, x1, offset_memory_operand (0)); + + /* Restore the previous state. */ + p += emit_add (p, sp, fp, immediate_operand (2 * 8)); + p += emit_ldp (p, fp, lr, fp, offset_memory_operand (0)); + + /* Return expr_eval_no_error. */ + p += emit_mov (p, x0, immediate_operand (expr_eval_no_error)); + p += emit_ret (p, lr); + + emit_ops_insns (buf, p - buf); +} + +/* Implementation of emit_ops method "emit_add". */ + +static void +aarch64_emit_add (void) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += emit_pop (p, x1); + p += emit_add (p, x0, x0, register_operand (x1)); + + emit_ops_insns (buf, p - buf); +} + +/* Implementation of emit_ops method "emit_sub". */ + +static void +aarch64_emit_sub (void) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += emit_pop (p, x1); + p += emit_sub (p, x0, x0, register_operand (x1)); + + emit_ops_insns (buf, p - buf); +} + +/* Implementation of emit_ops method "emit_mul". */ + +static void +aarch64_emit_mul (void) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += emit_pop (p, x1); + p += emit_mul (p, x0, x1, x0); + + emit_ops_insns (buf, p - buf); +} + +/* Implementation of emit_ops method "emit_lsh". */ + +static void +aarch64_emit_lsh (void) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += emit_pop (p, x1); + p += emit_lslv (p, x0, x1, x0); + + emit_ops_insns (buf, p - buf); +} + +/* Implementation of emit_ops method "emit_rsh_signed". */ + +static void +aarch64_emit_rsh_signed (void) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += emit_pop (p, x1); + p += emit_asrv (p, x0, x1, x0); + + emit_ops_insns (buf, p - buf); +} + +/* Implementation of emit_ops method "emit_rsh_unsigned". */ + +static void +aarch64_emit_rsh_unsigned (void) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += emit_pop (p, x1); + p += emit_lsrv (p, x0, x1, x0); + + emit_ops_insns (buf, p - buf); +} + +/* Implementation of emit_ops method "emit_ext". */ + +static void +aarch64_emit_ext (int arg) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += emit_sbfx (p, x0, x0, 0, arg); + + emit_ops_insns (buf, p - buf); +} + +/* Implementation of emit_ops method "emit_log_not". */ + +static void +aarch64_emit_log_not (void) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + /* If the top of the stack is 0, replace it with 1. Else replace it with + 0. */ + + p += emit_cmp (p, x0, immediate_operand (0)); + p += emit_cset (p, x0, EQ); + + emit_ops_insns (buf, p - buf); +} + +/* Implementation of emit_ops method "emit_bit_and". */ + +static void +aarch64_emit_bit_and (void) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += emit_pop (p, x1); + p += emit_and (p, x0, x0, x1); + + emit_ops_insns (buf, p - buf); +} + +/* Implementation of emit_ops method "emit_bit_or". */ + +static void +aarch64_emit_bit_or (void) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += emit_pop (p, x1); + p += emit_orr (p, x0, x0, x1); + + emit_ops_insns (buf, p - buf); +} + +/* Implementation of emit_ops method "emit_bit_xor". */ + +static void +aarch64_emit_bit_xor (void) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += emit_pop (p, x1); + p += emit_eor (p, x0, x0, x1); + + emit_ops_insns (buf, p - buf); +} + +/* Implementation of emit_ops method "emit_bit_not". */ + +static void +aarch64_emit_bit_not (void) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += emit_mvn (p, x0, x0); + + emit_ops_insns (buf, p - buf); +} + +/* Implementation of emit_ops method "emit_equal". */ + +static void +aarch64_emit_equal (void) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += emit_pop (p, x1); + p += emit_cmp (p, x0, register_operand (x1)); + p += emit_cset (p, x0, EQ); + + emit_ops_insns (buf, p - buf); +} + +/* Implementation of emit_ops method "emit_less_signed". */ + +static void +aarch64_emit_less_signed (void) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += emit_pop (p, x1); + p += emit_cmp (p, x1, register_operand (x0)); + p += emit_cset (p, x0, LT); + + emit_ops_insns (buf, p - buf); +} + +/* Implementation of emit_ops method "emit_less_unsigned". */ + +static void +aarch64_emit_less_unsigned (void) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += emit_pop (p, x1); + p += emit_cmp (p, x1, register_operand (x0)); + p += emit_cset (p, x0, LO); + + emit_ops_insns (buf, p - buf); +} + +/* Implementation of emit_ops method "emit_ref". */ + +static void +aarch64_emit_ref (int size) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + switch (size) + { + case 1: + p += emit_ldrb (p, w0, x0, offset_memory_operand (0)); + break; + case 2: + p += emit_ldrh (p, w0, x0, offset_memory_operand (0)); + break; + case 4: + p += emit_ldr (p, w0, x0, offset_memory_operand (0)); + break; + case 8: + p += emit_ldr (p, x0, x0, offset_memory_operand (0)); + break; + default: + /* Unknown size, bail on compilation. */ + emit_error = 1; + break; + } + + emit_ops_insns (buf, p - buf); +} + +/* Implementation of emit_ops method "emit_if_goto". */ + +static void +aarch64_emit_if_goto (int *offset_p, int *size_p) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + /* The Z flag is set or cleared here. */ + p += emit_cmp (p, x0, immediate_operand (0)); + /* This instruction must not change the Z flag. */ + p += emit_pop (p, x0); + /* Branch over the next instruction if x0 == 0. */ + p += emit_bcond (p, EQ, 8); + + /* The NOP instruction will be patched with an unconditional branch. */ + if (offset_p) + *offset_p = (p - buf) * 4; + if (size_p) + *size_p = 4; + p += emit_nop (p); + + emit_ops_insns (buf, p - buf); +} + +/* Implementation of emit_ops method "emit_goto". */ + +static void +aarch64_emit_goto (int *offset_p, int *size_p) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + /* The NOP instruction will be patched with an unconditional branch. */ + if (offset_p) + *offset_p = 0; + if (size_p) + *size_p = 4; + p += emit_nop (p); + + emit_ops_insns (buf, p - buf); +} + +/* Implementation of emit_ops method "write_goto_address". */ + +void +aarch64_write_goto_address (CORE_ADDR from, CORE_ADDR to, int size) +{ + uint32_t insn; + + emit_b (&insn, 0, to - from); + append_insns (&from, 1, &insn); +} + +/* Implementation of emit_ops method "emit_const". */ + +static void +aarch64_emit_const (LONGEST num) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += emit_mov_addr (p, x0, num); + + emit_ops_insns (buf, p - buf); +} + +/* Implementation of emit_ops method "emit_call". */ + +static void +aarch64_emit_call (CORE_ADDR fn) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += emit_mov_addr (p, ip0, fn); + p += emit_blr (p, ip0); + + emit_ops_insns (buf, p - buf); +} + +/* Implementation of emit_ops method "emit_reg". */ + +static void +aarch64_emit_reg (int reg) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + /* Set x0 to unsigned char *regs. */ + p += emit_sub (p, x0, fp, immediate_operand (2 * 8)); + p += emit_ldr (p, x0, x0, offset_memory_operand (0)); + p += emit_mov (p, x1, immediate_operand (reg)); + + emit_ops_insns (buf, p - buf); + + aarch64_emit_call (get_raw_reg_func_addr ()); +} + +/* Implementation of emit_ops method "emit_pop". */ + +static void +aarch64_emit_pop (void) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += emit_pop (p, x0); + + emit_ops_insns (buf, p - buf); +} + +/* Implementation of emit_ops method "emit_stack_flush". */ + +static void +aarch64_emit_stack_flush (void) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += emit_push (p, x0); + + emit_ops_insns (buf, p - buf); +} + +/* Implementation of emit_ops method "emit_zero_ext". */ + +static void +aarch64_emit_zero_ext (int arg) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += emit_ubfx (p, x0, x0, 0, arg); + + emit_ops_insns (buf, p - buf); +} + +/* Implementation of emit_ops method "emit_swap". */ + +static void +aarch64_emit_swap (void) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += emit_ldr (p, x1, sp, offset_memory_operand (0 * 16)); + p += emit_str (p, x0, sp, offset_memory_operand (0 * 16)); + p += emit_mov (p, x0, register_operand (x1)); + + emit_ops_insns (buf, p - buf); +} + +/* Implementation of emit_ops method "emit_stack_adjust". */ + +static void +aarch64_emit_stack_adjust (int n) +{ + /* This is not needed with our design. */ + uint32_t buf[16]; + uint32_t *p = buf; + + p += emit_add (p, sp, sp, immediate_operand (n * 16)); + + emit_ops_insns (buf, p - buf); +} + +/* Implementation of emit_ops method "emit_int_call_1". */ + +static void +aarch64_emit_int_call_1 (CORE_ADDR fn, int arg1) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += emit_mov (p, x0, immediate_operand (arg1)); + + emit_ops_insns (buf, p - buf); + + aarch64_emit_call (fn); +} + +/* Implementation of emit_ops method "emit_void_call_2". */ + +static void +aarch64_emit_void_call_2 (CORE_ADDR fn, int arg1) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + /* Push x0 on the stack. */ + aarch64_emit_stack_flush (); + + /* Setup arguments for the function call: + + x0: arg1 + x1: top of the stack + + MOV x1, x0 + MOV x0, #arg1 */ + + p += emit_mov (p, x1, register_operand (x0)); + p += emit_mov (p, x0, immediate_operand (arg1)); + + emit_ops_insns (buf, p - buf); + + aarch64_emit_call (fn); + + /* Restore x0. */ + aarch64_emit_pop (); +} + +/* Implementation of emit_ops method "emit_eq_goto". */ + +static void +aarch64_emit_eq_goto (int *offset_p, int *size_p) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += emit_pop (p, x1); + p += emit_cmp (p, x1, register_operand (x0)); + /* Branch over the next instruction if x0 != x1. */ + p += emit_bcond (p, NE, 8); + /* The NOP instruction will be patched with an unconditional branch. */ + if (offset_p) + *offset_p = (p - buf) * 4; + if (size_p) + *size_p = 4; + p += emit_nop (p); + + emit_ops_insns (buf, p - buf); +} + +/* Implementation of emit_ops method "emit_ne_goto". */ + +static void +aarch64_emit_ne_goto (int *offset_p, int *size_p) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += emit_pop (p, x1); + p += emit_cmp (p, x1, register_operand (x0)); + /* Branch over the next instruction if x0 == x1. */ + p += emit_bcond (p, EQ, 8); + /* The NOP instruction will be patched with an unconditional branch. */ + if (offset_p) + *offset_p = (p - buf) * 4; + if (size_p) + *size_p = 4; + p += emit_nop (p); + + emit_ops_insns (buf, p - buf); +} + +/* Implementation of emit_ops method "emit_lt_goto". */ + +static void +aarch64_emit_lt_goto (int *offset_p, int *size_p) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += emit_pop (p, x1); + p += emit_cmp (p, x1, register_operand (x0)); + /* Branch over the next instruction if x0 >= x1. */ + p += emit_bcond (p, GE, 8); + /* The NOP instruction will be patched with an unconditional branch. */ + if (offset_p) + *offset_p = (p - buf) * 4; + if (size_p) + *size_p = 4; + p += emit_nop (p); + + emit_ops_insns (buf, p - buf); +} + +/* Implementation of emit_ops method "emit_le_goto". */ + +static void +aarch64_emit_le_goto (int *offset_p, int *size_p) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += emit_pop (p, x1); + p += emit_cmp (p, x1, register_operand (x0)); + /* Branch over the next instruction if x0 > x1. */ + p += emit_bcond (p, GT, 8); + /* The NOP instruction will be patched with an unconditional branch. */ + if (offset_p) + *offset_p = (p - buf) * 4; + if (size_p) + *size_p = 4; + p += emit_nop (p); + + emit_ops_insns (buf, p - buf); +} + +/* Implementation of emit_ops method "emit_gt_goto". */ + +static void +aarch64_emit_gt_goto (int *offset_p, int *size_p) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += emit_pop (p, x1); + p += emit_cmp (p, x1, register_operand (x0)); + /* Branch over the next instruction if x0 <= x1. */ + p += emit_bcond (p, LE, 8); + /* The NOP instruction will be patched with an unconditional branch. */ + if (offset_p) + *offset_p = (p - buf) * 4; + if (size_p) + *size_p = 4; + p += emit_nop (p); + + emit_ops_insns (buf, p - buf); +} + +/* Implementation of emit_ops method "emit_ge_got". */ + +static void +aarch64_emit_ge_got (int *offset_p, int *size_p) +{ + uint32_t buf[16]; + uint32_t *p = buf; + + p += emit_pop (p, x1); + p += emit_cmp (p, x1, register_operand (x0)); + /* Branch over the next instruction if x0 <= x1. */ + p += emit_bcond (p, LT, 8); + /* The NOP instruction will be patched with an unconditional branch. */ + if (offset_p) + *offset_p = (p - buf) * 4; + if (size_p) + *size_p = 4; + p += emit_nop (p); + + emit_ops_insns (buf, p - buf); +} + +static struct emit_ops aarch64_emit_ops_impl = +{ + aarch64_emit_prologue, + aarch64_emit_epilogue, + aarch64_emit_add, + aarch64_emit_sub, + aarch64_emit_mul, + aarch64_emit_lsh, + aarch64_emit_rsh_signed, + aarch64_emit_rsh_unsigned, + aarch64_emit_ext, + aarch64_emit_log_not, + aarch64_emit_bit_and, + aarch64_emit_bit_or, + aarch64_emit_bit_xor, + aarch64_emit_bit_not, + aarch64_emit_equal, + aarch64_emit_less_signed, + aarch64_emit_less_unsigned, + aarch64_emit_ref, + aarch64_emit_if_goto, + aarch64_emit_goto, + aarch64_write_goto_address, + aarch64_emit_const, + aarch64_emit_call, + aarch64_emit_reg, + aarch64_emit_pop, + aarch64_emit_stack_flush, + aarch64_emit_zero_ext, + aarch64_emit_swap, + aarch64_emit_stack_adjust, + aarch64_emit_int_call_1, + aarch64_emit_void_call_2, + aarch64_emit_eq_goto, + aarch64_emit_ne_goto, + aarch64_emit_lt_goto, + aarch64_emit_le_goto, + aarch64_emit_gt_goto, + aarch64_emit_ge_got, +}; + +/* Implementation of linux_target_ops method "emit_ops". */ + +static struct emit_ops * +aarch64_emit_ops (void) +{ + return &aarch64_emit_ops_impl; +} + /* Implementation of linux_target_ops method "get_min_fast_tracepoint_insn_len". */ @@ -2058,7 +3257,7 @@ struct linux_target_ops the_low_target = aarch64_supports_tracepoints, aarch64_get_thread_area, aarch64_install_fast_tracepoint_jump_pad, - NULL, /* emit_ops */ + aarch64_emit_ops, aarch64_get_min_fast_tracepoint_insn_len, aarch64_supports_range_stepping, }; -- 2.30.2