From patchwork Tue Jul 5 13:40:24 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Antoine Tremblay X-Patchwork-Id: 13652 Received: (qmail 67195 invoked by alias); 5 Jul 2016 13:41:59 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 67144 invoked by uid 89); 5 Jul 2016 13:41:59 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-0.9 required=5.0 tests=BAYES_00, KAM_LAZY_DOMAIN_SECURITY autolearn=no version=3.3.2 spammy=MRS, 20152016, 2015-2016, rel X-HELO: usplmg21.ericsson.net Received: from usplmg21.ericsson.net (HELO usplmg21.ericsson.net) (198.24.6.65) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES256-SHA encrypted) ESMTPS; Tue, 05 Jul 2016 13:41:31 +0000 Received: from EUSAAHC008.ericsson.se (Unknown_Domain [147.117.188.96]) by usplmg21.ericsson.net (Symantec Mail Security) with SMTP id 8A.66.03614.1B8BB775; Tue, 5 Jul 2016 15:40:01 +0200 (CEST) Received: from elxa4wqvvz1.dyn.mo.ca.am.ericsson.se (147.117.188.8) by smtps-am.internal.ericsson.com (147.117.188.96) with Microsoft SMTP Server (TLS) id 14.3.294.0; Tue, 5 Jul 2016 09:40:54 -0400 From: Antoine Tremblay To: CC: Antoine Tremblay Subject: [PATCH v3 12/18] Add ARM/Thumb instruction assembler for fast tracepoints Date: Tue, 5 Jul 2016 09:40:24 -0400 Message-ID: <1467726030-13020-13-git-send-email-antoine.tremblay@ericsson.com> In-Reply-To: <1467726030-13020-1-git-send-email-antoine.tremblay@ericsson.com> References: <1467726030-13020-1-git-send-email-antoine.tremblay@ericsson.com> MIME-Version: 1.0 X-IsSubscribed: yes This patch adds the required helper functions to emit native ARM and Thumb instructions for use in the fast tracepoints jump pad and in the fast tracepoints JIT condition implementation. gdb/ChangeLog: * Makefile.in (ALL_64_TARGET_OBS): Add arm-insn-utils.o (ALL_TARGET_OBS): Add arm-insn-emit.o. (HFILES_NO_SRCDIR): Add arch/arm-insn-emit.h, arch/arm-insn-utils.h. (arm-linux.o:): Add rule. (arm-insn-emit.o): Likewise. (arm-insn-utils.o): Likewise. * aarch64-tdep.c: Include arch/arm-insn-utils.h (aarch64_displaced_step_ldr_literal): Adapt to sue arm_memory_operand. * arch/aarch64-insn.c: Include arch/arm-insn-utils.h (aarch64_emit_load_store): Adapt to use arm_memory_operand. * arch/aarch64-insn.h (struct aarch64_register): Likewise. (enum aarch64_memory_operand_type): Remove. (struct aarch64_memory_operand): Remove. (ENCODE): Move to arm-insn-utils.h. (aarch64_emit_load_store): Adapt to use arm_memory_operand. * arch/arm-insn-emit.c: New file. * arch/arm-insn-emit.h: New file. * configure.tgt (aarch64*-*-elf | aarch64*-*-rtems*): Add arm-insn-utils.o (aarch64*-*-linux*): Likewise. gdb/gdbserver/ChangeLog: * Makefile.in (SFILES): Add arch/arm-insn-emit.c. (arm-insn-emit.o): New rule. (arm-insn-utils.o): New rule. * configure.srv (aarch64*-*-linux*): Add arm-insn-utils.o. (arm*-*-linux*): Likewise. * linux-aarch64-low.c: Include arch/arm-insn-utils.h. (enum aarch64_operand_type): Remove. (struct aarch64_operand): Adapt for arm_operand_type. (offset_memory_operand): Move to arm-insn-utils.c. (preindex_memory_operand): Likewise. (postindex_memory_operand): Likewise. (emit_load_store_pair): Adapt for arm_memory_operand. (emit_load_store_pair): Likewise. (emit_ldp): Likewise. (emit_ldrh): Likewise. (emit_ldrb): Likewise. (emit_str): Likewise. --- gdb/Makefile.in | 18 +- gdb/aarch64-tdep.c | 3 +- gdb/arch/aarch64-insn.c | 3 +- gdb/arch/aarch64-insn.h | 32 +- gdb/arch/arm-insn-emit.c | 1550 +++++++++++++++++++++++++++++++++++++ gdb/arch/arm-insn-emit.h | 1087 ++++++++++++++++++++++++++ gdb/arch/arm-insn-utils.c | 44 ++ gdb/arch/arm-insn-utils.h | 80 ++ gdb/configure.tgt | 5 +- gdb/gdbserver/Makefile.in | 10 +- gdb/gdbserver/configure.srv | 3 + gdb/gdbserver/linux-aarch64-low.c | 54 +- 12 files changed, 2804 insertions(+), 85 deletions(-) create mode 100644 gdb/arch/arm-insn-emit.c create mode 100644 gdb/arch/arm-insn-emit.h create mode 100644 gdb/arch/arm-insn-utils.c create mode 100644 gdb/arch/arm-insn-utils.h diff --git a/gdb/Makefile.in b/gdb/Makefile.in index a83b456..fe5b6f8 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -644,7 +644,9 @@ TARGET_OBS = @TARGET_OBS@ # All target-dependent objects files that require 64-bit CORE_ADDR # (used with --enable-targets=all --enable-64-bit-bfd). ALL_64_TARGET_OBS = \ - aarch64-tdep.o aarch64-linux-tdep.o aarch64-newlib-tdep.o aarch64-insn.o \ + aarch64-tdep.o aarch64-linux-tdep.o aarch64-newlib-tdep.o \ + aarch64-insn.o \ + arm-insn-utils.o \ alphabsd-tdep.o alphafbsd-tdep.o alpha-linux-tdep.o alpha-mdebug-tdep.o \ alphanbsd-tdep.o alphaobsd-tdep.o alpha-tdep.o \ amd64fbsd-tdep.o amd64-darwin-tdep.o amd64-dicos-tdep.o \ @@ -662,8 +664,9 @@ ALL_TARGET_OBS = \ arm-linux.o \ arm-linux-tdep.o \ arm-get-next-pcs.o \ - arm-symbian-tdep.o \ + arm-insn-emit.o \ arm-insn-reloc.o \ + arm-symbian-tdep.o \ armnbsd-tdep.o armobsd-tdep.o \ arm-tdep.o arm-wince-tdep.o \ avr-tdep.o \ @@ -994,7 +997,8 @@ common/common-exceptions.h target/target.h common/symbol.h \ common/common-regcache.h fbsd-tdep.h nat/linux-personality.h \ common/fileio.h nat/x86-linux.h nat/x86-linux-dregs.h nat/amd64-linux-siginfo.h\ nat/linux-namespaces.h arch/arm.h common/gdb_sys_time.h arch/aarch64-insn.h \ -tid-parse.h ser-event.h arch/arm-insn-reloc.h +tid-parse.h ser-event.h arch/arm-insn-emit.h arch/arm-insn-reloc.h \ +arch/arm-insn-utils.h # Header files that already have srcdir in them, or which are in objdir. @@ -2315,6 +2319,10 @@ arm-linux.o: ${srcdir}/arch/arm-linux.c $(COMPILE) $(srcdir)/arch/arm-linux.c $(POSTCOMPILE) +arm-insn-emit.o: ${srcdir}/arch/arm-insn-emit.c + $(COMPILE) $(srcdir)/arch/arm-insn-emit.c + $(POSTCOMPILE) + arm-insn-reloc.o: ${srcdir}/arch/arm-insn-reloc.c $(COMPILE) $(srcdir)/arch/arm-insn-reloc.c $(POSTCOMPILE) @@ -2397,6 +2405,10 @@ aarch64-insn.o: ${srcdir}/arch/aarch64-insn.c $(COMPILE) $(srcdir)/arch/aarch64-insn.c $(POSTCOMPILE) +arm-insn-utils.o: ${srcdir}/arch/arm-insn-utils.c + $(COMPILE) $(srcdir)/arch/arm-insn-utils.c + $(POSTCOMPILE) + # # gdb/tui/ dependencies # diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c index e5ce13e..67c10f2 100644 --- a/gdb/aarch64-tdep.c +++ b/gdb/aarch64-tdep.c @@ -57,6 +57,7 @@ #include "features/aarch64.c" +#include "arch/arm-insn-utils.h" #include "arch/aarch64-insn.h" #include "opcode/aarch64.h" @@ -2471,7 +2472,7 @@ aarch64_displaced_step_ldr_literal (const int32_t offset, const int is_sw, struct aarch64_displaced_step_data *dsd = (struct aarch64_displaced_step_data *) data; CORE_ADDR address = data->insn_addr + offset; - struct aarch64_memory_operand zero = { MEMORY_OPERAND_OFFSET, 0 }; + struct arm_memory_operand zero = { MEMORY_OPERAND_OFFSET, 0 }; regcache_cooked_write_unsigned (dsd->regs, AARCH64_X0_REGNUM + rt, address); diff --git a/gdb/arch/aarch64-insn.c b/gdb/arch/aarch64-insn.c index 772512b..88fbffe 100644 --- a/gdb/arch/aarch64-insn.c +++ b/gdb/arch/aarch64-insn.c @@ -17,6 +17,7 @@ along with this program. If not, see . */ #include "common-defs.h" +#include "arm-insn-utils.h" #include "aarch64-insn.h" /* Toggle this file's internal debugging dump. */ @@ -346,7 +347,7 @@ aarch64_emit_load_store (uint32_t *buf, uint32_t size, enum aarch64_opcodes opcode, struct aarch64_register rt, struct aarch64_register rn, - struct aarch64_memory_operand operand) + struct arm_memory_operand operand) { uint32_t op; diff --git a/gdb/arch/aarch64-insn.h b/gdb/arch/aarch64-insn.h index a799515..511a80c 100644 --- a/gdb/arch/aarch64-insn.h +++ b/gdb/arch/aarch64-insn.h @@ -117,36 +117,6 @@ struct aarch64_register int is64; }; -enum aarch64_memory_operand_type -{ - MEMORY_OPERAND_OFFSET, - MEMORY_OPERAND_PREINDEX, - MEMORY_OPERAND_POSTINDEX, -}; - -/* Representation of a memory operand, used for load and store - instructions. - - The types correspond to the following variants: - - 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 -{ - /* Type of the operand. */ - enum aarch64_memory_operand_type type; - - /* Index from the base register. */ - int32_t index; -}; - -/* Helper macro to mask and shift a value into a bitfield. */ - -#define ENCODE(val, size, offset) \ - ((uint32_t) ((val & ((1ULL << size) - 1)) << offset)) - int aarch64_decode_adr (CORE_ADDR addr, uint32_t insn, int *is_adrp, unsigned *rd, int32_t *offset); @@ -319,6 +289,6 @@ int aarch64_emit_load_store (uint32_t *buf, uint32_t size, enum aarch64_opcodes opcode, struct aarch64_register rt, struct aarch64_register rn, - struct aarch64_memory_operand operand); + struct arm_memory_operand operand); #endif diff --git a/gdb/arch/arm-insn-emit.c b/gdb/arch/arm-insn-emit.c new file mode 100644 index 0000000..810172d --- /dev/null +++ b/gdb/arch/arm-insn-emit.c @@ -0,0 +1,1550 @@ +/* Copyright (C) 2015-2016 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include "common-defs.h" +#include "arm.h" +#include "arm-insn-utils.h" +#include "arm-insn-emit.h" + +#define ABS(x) ((x) < 0 ? -(x) : (x)) + +/* Encode a bitfield with a repetition of BIT starting at from FROM for + LENGHT. Max 16 bits. */ + +static uint16_t +repeat_bit (uint8_t bit, uint8_t from, uint8_t length) +{ + uint16_t value = 0; + int i; + + for (i = from; i - from < length; i++) + { + value |= ENCODE (bit & 1, 1, i); + } + + return value; +} + +/* See arm-insn-emit.h. */ + +uint16_t +encode_register_list (uint8_t from, uint8_t length, uint16_t initial) +{ + return repeat_bit (1, from, length) | initial; +} + +/* See arm-insn-emit.h. */ + +struct arm_operand +immediate_operand (uint32_t imm) +{ + struct arm_operand operand; + + operand.type = OPERAND_IMMEDIATE; + operand.imm = imm; + + return operand; +} + +/* Helper function to create a register operand, for instructions with + different types of operands. + + For example: + p += emit_mov (p, x0, register_operand (x1)); */ + +/* See arm-insn-emit.h. */ + +struct arm_operand +register_operand (uint8_t reg) +{ + struct arm_operand operand; + + operand.type = OPERAND_REGISTER; + operand.reg = reg; + + return operand; +} + +/* See arm-insn-emit.h. */ + +struct arm_operand +memory_operand (struct arm_memory_operand mem) +{ + struct arm_operand operand; + + operand.type = OPERAND_MEMORY; + operand.mem = mem; + + return operand; +} + +/* Write a 32-bit unsigned integer INSN info *BUF. Return the number of + instructions written (aka. 1). */ + +static int +arm_emit_arm_insn (uint32_t *buf, uint32_t insn) +{ + *buf = insn; + return 1; +} + +/* Write a 16-bit unsigned integer INSN info *BUF. Return the number of + instructions written (aka. 1). */ + +static int +arm_emit_thumb_insn (uint16_t *buf, uint16_t insn) +{ + *buf = insn; + return 1; +} + +/* Write a 32-bit unsigned integer INSN representing a Thumb-2 wide + instruction info *BUF. Return the number of instructions written + (aka. 2). */ + +static int +arm_emit_thumb_w_insn (uint16_t *buf, uint32_t insn) +{ + int res = arm_emit_thumb_insn (buf, bits (insn, 16, 31)); + res += arm_emit_thumb_insn (buf + res, bits (insn, 0, 15)); + return res; +} + +/* See arm-insn-emit.h. */ + +uint32_t +arm_arm_branch_relative_distance (CORE_ADDR from, CORE_ADDR to) +{ + return arm_arm_branch_adjusted_offset ((uint32_t) to - (uint32_t) from); +} + +/* See arm-insn-emit.h. */ + +uint32_t +arm_thumb_branch_relative_distance (CORE_ADDR from, CORE_ADDR to) +{ + uint32_t from_ = ((uint32_t) from) & ~1; + uint32_t to_ = ((uint32_t) to) & ~1; + return arm_thumb_branch_adjusted_offset (to_ - from_); +} + +/* See arm-insn-emit.h. */ + +uint32_t +arm_thumb_to_arm_branch_relative_distance (CORE_ADDR from, CORE_ADDR to) +{ + uint32_t from_ = ((uint32_t) from) & ~3; + uint32_t to_ = ((uint32_t) to) & ~3; + return arm_thumb_branch_adjusted_offset (to_ - from_); +} + +/* See arm-insn-emit.h. */ + +int +arm_arm_is_reachable (CORE_ADDR from, CORE_ADDR to) +{ + int32_t rel = arm_arm_branch_relative_distance (from, to); + rel >>= 25; + return !rel || !(rel + 1); +} + +/* See arm-insn-emit.h. */ + +int +arm_thumb_is_reachable (CORE_ADDR from, CORE_ADDR to) +{ + int32_t rel = arm_thumb_branch_relative_distance (from, to); + rel >>= 24; + return !rel || !(rel + 1); +} + +/* See arm-insn-emit.h. */ + +uint32_t +arm_arm_branch_adjusted_offset (uint32_t offset) +{ + return offset - 8; +} + +/* See arm-insn-emit.h. */ + +uint32_t +arm_thumb_branch_adjusted_offset (uint32_t offset) +{ + return offset - 4; +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_thumb_mov_32 (uint16_t *mem, int reg, uint32_t val) +{ + uint16_t val_low = bits (val, 0, 15); + uint16_t val_high = bits (val, 16, 31); + int res = 0; + + res += arm_emit_thumb_movw (mem, reg, immediate_operand (val_low)); + res += arm_emit_thumb_movt (mem + res, reg, immediate_operand (val_high)); + + return res; +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_arm_mov_32 (uint32_t *mem, int reg, uint32_t val) +{ + uint16_t val_low = bits (val, 0, 15); + uint16_t val_high = bits (val, 16, 31); + int res = 0; + + res += arm_emit_arm_movw (mem, INST_AL, reg, immediate_operand (val_low)); + res += arm_emit_arm_movt (mem + res, INST_AL, reg, + immediate_operand (val_high)); + + return res; +} + +/* Write ARM branch instructions into *BUF. + + This is a base function to write + B, BL and BLX instructions with immediate or register operands. + + Proper encodings documentation is provided in the + arm_emit_arm_{b_,bl_,blx} helper functions. + + L if set a bl instruction will be written. + X if set a blx instruction will be written. */ + +static int +arm_emit_arm_branch (uint32_t *buf, uint8_t cond, + struct arm_operand operand, + uint8_t l, uint8_t x) +{ + if (operand.type == OPERAND_IMMEDIATE) + { + /* BLX */ + if (l == 1 && x == 1) + { + cond = 0xF; + l = bit (operand.imm, 1); + } + /* Encode B, BL or BLX. */ + return arm_emit_arm_insn (buf, (ARM_B + | ENCODE (l, 1, 24) + | ENCODE (cond, 4, 28) + | ENCODE (operand.imm >> 2, 24, 0))); + } + else if (operand.type == OPERAND_REGISTER) + { + if (l == 1 && x == 1) + { + return arm_emit_arm_insn (buf, (ARM_BLX + | ENCODE (cond, 4, 28) + | ENCODE (operand.reg, 4, 0))); + } + else + { + /* error. Only BLX has a register operand. */ + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } + } + else + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } +} + +/* Write Thumb branch instructions into *BUF. + + This is a base function to write + BW, BL and BLX instructions with immediate or register operand. + + Proper encodings documentation is provided in the + arm_emit_thumb_{bw_,bl_,blx} helper functions. + + REL is the relative offset of the label. + L if set a bl instruction will be written. + X if set a blx instruction will be written. */ + +int +arm_emit_thumb_branch (uint16_t *buf, struct arm_operand operand, + uint8_t l, uint8_t x) +{ + if (operand.type == OPERAND_IMMEDIATE) + { + uint32_t imm10, imm11; + uint32_t s, j1, j2; + + if (x == 0) + imm11 = bits (operand.imm, 1, 11); + else + /* IMM10L:H. */ + imm11 = bits (operand.imm, 2, 11) << 1; + + imm10 = bits (operand.imm, 12, 21); + s = bit (operand.imm, 24); + j1 = s ^ !(bit (operand.imm, 23)); + j2 = s ^ !(bit (operand.imm, 22)); + + return arm_emit_thumb_w_insn (buf, (THUMB_BW + | ENCODE (s, 1, 26) + | ENCODE (imm10, 10, 16) + | ENCODE (l, 1, 14) + | ENCODE (j1, 1, 13) + | ENCODE (x == 0 ? 1 : 0, 1, 12) + | ENCODE (j2, 1, 11) + | ENCODE (imm11, 11, 0))); + } + else if (operand.type == OPERAND_REGISTER) + { + if (l == 1 && x == 1) + return arm_emit_thumb_insn (buf, (THUMB_BLX + | ENCODE (operand.reg, 4, 3))); + else + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } + } + else + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } +} + + +/* See arm-insn-emit.h. */ + +int +arm_emit_arm_b (uint32_t *buf, uint8_t cond, uint32_t rel) +{ + return arm_emit_arm_branch (buf, cond, immediate_operand (rel), 0, 0); +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_arm_bl (uint32_t *buf, uint8_t cond, uint32_t rel) +{ + return arm_emit_arm_branch (buf, cond, immediate_operand (rel), 1, 0); +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_thumb_bl (uint16_t *buf, uint32_t rel) +{ + return arm_emit_thumb_branch (buf, immediate_operand (rel), 1, 0); +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_thumb_b (uint16_t *buf, uint8_t cond, uint32_t rel) +{ + return arm_emit_thumb_insn (buf, (THUMB_B + | ENCODE (cond, 4, 8) + | ENCODE (rel >> 1, 8, 0))); +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_thumb_bw (uint16_t *buf, uint32_t rel) +{ + return arm_emit_thumb_branch (buf, immediate_operand (rel), 0, 0); +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_thumb_bw_cond (uint16_t *buf, uint8_t cond, uint32_t rel) +{ + uint32_t imm6, imm11; + uint32_t s, j1, j2; + + imm11 = bits (rel, 1, 11); + imm6 = bits (rel, 12, 17); + s = bit (rel, 24); + j1 = s ^ !(bit (rel, 23)); + j2 = s ^ !(bit (rel, 22)); + + return arm_emit_thumb_w_insn (buf, (THUMB_BW + | ENCODE (s, 1, 26) + | ENCODE (imm6, 6, 16) + | ENCODE (cond, 4, 22) + | ENCODE (j1, 1, 13) + | ENCODE (j2, 1, 11) + | ENCODE (imm11, 11, 0))); +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_thumb_blx (uint16_t *buf, struct arm_operand operand) +{ + return arm_emit_thumb_branch (buf, operand, 1, 1); +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_arm_blx (uint32_t *buf, uint8_t cond, struct arm_operand operand) +{ + return arm_emit_arm_branch (buf, cond, operand, 1, 1); +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_arm_movw (uint32_t *buf, uint8_t cond, uint8_t rd, + struct arm_operand operand) +{ + if (operand.type == OPERAND_IMMEDIATE) + { + return arm_emit_arm_insn (buf, + (ARM_MOVW + | ENCODE (cond, 4, 28) + | ENCODE (bits (operand.imm, 12, 15), 4, 16) + | ENCODE (rd, 4, 12) + | ENCODE (bits (operand.imm, 0, 11), 12, 0))); + } + else + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_arm_mov (uint32_t *buf, uint8_t cond, uint8_t rd, + struct arm_operand operand) +{ + if (operand.type == OPERAND_IMMEDIATE) + { + return arm_emit_arm_insn (buf, + (ARM_MOV + | ENCODE (cond, 4, 28) + /* Immediate value opcode. */ + | ENCODE (1, 1, 25) + | ENCODE (rd, 4, 12) + | ENCODE (bits (operand.imm, 0, 11), 12, 0))); + } + else if (operand.type == OPERAND_REGISTER) + { + return arm_emit_arm_insn (buf, (ARM_MOV + | ENCODE (cond, 4, 28) + | ENCODE (rd, 4, 12) + | ENCODE (operand.reg, 4, 0))); + } + else + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_thumb_movw (uint16_t *buf, uint8_t rd, struct arm_operand operand) +{ + if (operand.type == OPERAND_IMMEDIATE) + { + return arm_emit_thumb_w_insn (buf, + (THUMB_MOVW + | ENCODE (bit (operand.imm, 11), 1, 26) + | ENCODE (bits (operand.imm, 12, 15), + 4, 16) + | ENCODE (bits (operand.imm, 8, 10), 3, 12) + | ENCODE (rd, 4, 8) + | ENCODE (bits (operand.imm, 0, 7), + 8, 0))); + } + else + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_arm_movt (uint32_t *buf, uint8_t cond, uint8_t rd, + struct arm_operand operand) +{ + if (operand.type == OPERAND_IMMEDIATE) + { + return arm_emit_arm_insn (buf, + (ARM_MOVT + | ENCODE (cond, 4, 28) + | ENCODE (bits (operand.imm, 12, 15), 4, 16) + | ENCODE (rd, 4, 12) + | ENCODE (bits (operand.imm, 0, 11), 12, 0))); + } + else + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_thumb_movt (uint16_t *buf, uint8_t rd, struct arm_operand operand) +{ + if (operand.type == OPERAND_IMMEDIATE) + { + return arm_emit_thumb_w_insn (buf, + (THUMB_MOVT + | ENCODE (bit (operand.imm, 11), 1, 26) + | ENCODE (bits (operand.imm, 12, 15), + 4, 16) + | ENCODE (bits (operand.imm, 8, 10), 3, 12) + | ENCODE (rd, 4, 8) + | ENCODE (bits (operand.imm, 0, 7),8, 0))); + } + else + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_arm_vpush (uint32_t *buf, uint8_t cond, uint8_t rs, uint8_t len) +{ + return arm_emit_arm_insn (buf, (ARM_VPUSH + | ENCODE (cond, 4, 28) + | ENCODE (bit (rs, 4), 1, 22) + | ENCODE (bits (rs, 0, 3), 4, 12) + | ENCODE (2 * len, 8, 0))); +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_thumb_vpush (uint16_t *buf, uint8_t rs, uint8_t len) +{ + return arm_emit_thumb_w_insn (buf, (THUMB_VPUSH + | ENCODE (bit (rs, 4), 1, 22) + | ENCODE (bits (rs, 0, 3), 4, 12) + | ENCODE (2 * len, 8, 0))); +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_arm_push_list (uint32_t *buf, uint8_t cond, uint16_t register_list) +{ + return arm_emit_arm_insn (buf, (ARM_PUSH_A1 + | ENCODE (cond, 4, 28) + | ENCODE (register_list, 16, 0))); +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_arm_push_one (uint32_t *buf, uint8_t cond, uint8_t rt) +{ + return arm_emit_arm_insn (buf, (ARM_PUSH_A2 + | ENCODE (cond, 4, 28) + | ENCODE (rt, 4, 12))); +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_thumb_push_one (uint16_t *buf, uint8_t register_list, uint8_t lr) +{ + return arm_emit_thumb_insn (buf, (THUMB_PUSH_T1 + | ENCODE (register_list, 8, 0) + | ENCODE (lr, 1, 8))); +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_thumb_push_list (uint16_t *buf, + uint16_t register_list, + uint8_t lr) +{ + return arm_emit_thumb_w_insn (buf, (THUMB_PUSH_T2 + | ENCODE (bit (lr, 0), 1, 14) + | ENCODE (register_list, 13, 0))); +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_arm_mrs (uint32_t *buf, + uint8_t cond, + uint8_t rd) +{ + return arm_emit_arm_insn (buf, (ARM_MRS + | ENCODE (cond, 4, 28) + | ENCODE (rd, 4, 12))); +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_thumb_mrs (uint16_t *buf, uint8_t rd) +{ + return arm_emit_thumb_w_insn (buf, THUMB_MRS | ENCODE (rd, 4, 12)); +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_thumb_mov (uint16_t *buf, uint8_t rd, struct arm_operand operand) +{ + if (operand.type == OPERAND_REGISTER) + { + return arm_emit_thumb_insn (buf, (THUMB_MOV + | ENCODE (bit (rd, 3), 1, 7) + | ENCODE (bits (rd, 0, 2), 3, 0) + | ENCODE (operand.reg, 4, 3))); + } + else + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_arm_dmb (uint32_t *buf) +{ + return arm_emit_arm_insn (buf, ARM_DMB | ENCODE (0xF, 4, 0)); + +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_thumb_dmb (uint16_t *buf) +{ + return arm_emit_thumb_w_insn (buf, THUMB_DMB | ENCODE (0xF, 4, 0)); +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_arm_ldrex (uint32_t *buf, uint8_t cond, uint8_t rt, uint8_t rn) +{ + return arm_emit_arm_insn (buf, (ARM_LDREX + | ENCODE (cond, 4, 28) + | ENCODE (rn, 4, 16) + | ENCODE (rt, 4, 12))); +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_thumb_ldrex (uint16_t *buf, int8_t rt, uint8_t rn, + struct arm_operand operand) +{ + + if (operand.type == OPERAND_IMMEDIATE) + { + return arm_emit_thumb_w_insn (buf, + (THUMB_LDREX + | ENCODE (rn, 4, 16) + | ENCODE (rt, 4, 12) + | ENCODE (bits (operand.imm, 0, 7), + 8, 0))); + } + else + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_arm_cmp (uint32_t *buf, uint8_t cond, uint8_t rn, + struct arm_operand operand) +{ + if (operand.type == OPERAND_IMMEDIATE) + { + return arm_emit_arm_insn (buf, + (ARM_CMP + | ENCODE (cond, 4, 28) + /* Immediate. */ + | ENCODE (1, 1, 25) + | ENCODE (rn, 4, 16) + | ENCODE (bits (operand.imm, 0, 11), 12, 0))); + } + else if (operand.type == OPERAND_REGISTER) + { + return arm_emit_arm_insn (buf, (ARM_CMP + | ENCODE (cond, 4, 28) + | ENCODE (rn, 4, 16) + | ENCODE (operand.reg, 4, 0))); + } + else + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_thumb_cmp (uint16_t *buf, uint8_t rn, struct arm_operand operand) +{ + if (operand.type == OPERAND_IMMEDIATE) + { + return arm_emit_thumb_insn (buf, + (THUMB_CMP + | ENCODE (rn, 3, 8) + | ENCODE (bits (operand.imm, 0, 7), 8, 0))); + } + else + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_thumb_cmpw (uint16_t *buf, uint8_t rn, struct arm_operand operand) +{ + if (operand.type == OPERAND_IMMEDIATE) + { + return arm_emit_thumb_w_insn (buf, + (THUMB_CMPW + | ENCODE (rn, 4, 16) + | ENCODE (bit (operand.imm, 11), 1, 26) + | ENCODE (bits (operand.imm, 8, 10), 3, 12) + | ENCODE (bits (operand.imm, 0, 7), + 8, 0))); + } + else + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_arm_bic (uint32_t *buf, uint8_t cond, uint8_t rd, uint8_t rn, + struct arm_operand operand) +{ + if (operand.type == OPERAND_IMMEDIATE) + { + return arm_emit_arm_insn (buf, + (ARM_BIC + | ENCODE (cond, 4, 28) + | ENCODE (rn, 4, 16) + | ENCODE (rd, 4, 12) + | ENCODE (bits (operand.imm, 0, 11), 12, 0))); + } + else + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_thumb_bic (uint16_t *buf, uint8_t rd, uint8_t rn, + struct arm_operand operand) +{ + if (operand.type == OPERAND_IMMEDIATE) + { + return arm_emit_thumb_w_insn (buf, + (THUMB_BIC + | ENCODE (bit (operand.imm, 11), 1, 26) + | ENCODE (rn, 4, 16) + | ENCODE (rd, 4, 8) + | ENCODE (bits (operand.imm, 8, 10), 3, 12) + | ENCODE (bits (operand.imm, 0, 7), + 8, 0))); + } + else + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_arm_strex (uint32_t *buf, uint8_t cond, uint8_t rd, uint8_t rt, + uint8_t rn) +{ + return arm_emit_arm_insn (buf, (ARM_STREX + | ENCODE (cond, 4, 28) + | ENCODE (rn, 4, 16) + | ENCODE (rd, 4, 12) + | ENCODE (rt, 4, 0))); +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_thumb_strex (uint16_t *buf, uint8_t rd, uint8_t rt, uint8_t rn, + struct arm_operand operand) +{ + if (operand.type == OPERAND_IMMEDIATE) + { + return arm_emit_thumb_w_insn (buf, (THUMB_STREX + | ENCODE (rn, 4, 16) + | ENCODE (rt, 4, 12) + | ENCODE (rd, 4, 8) + | ENCODE (operand.imm, 8, 0))); + } + else + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_arm_str (uint32_t *buf, uint8_t cond, uint8_t rt, uint8_t rn, + struct arm_operand operand) +{ + if (operand.type == OPERAND_MEMORY) + { + switch (operand.mem.type) + { + case MEMORY_OPERAND_OFFSET: + { + return arm_emit_arm_insn (buf, + (ARM_STR + | ENCODE (cond, 4, 28) + /* P. */ + | ENCODE (1, 1, 24) + /* U. */ + | ENCODE (operand.mem.index < 0 ? 0 : 1, + 1, 23) + | ENCODE (rn, 4, 16) + | ENCODE (rt, 4, 12) + | ENCODE (bits (ABS (operand.mem.index), + 0, 11), 12, 0))); + + } + default: + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } + } + } + else + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_thumb_str (uint16_t *buf, uint8_t rt, uint8_t rn, + struct arm_operand operand) +{ + if (operand.type == OPERAND_IMMEDIATE) + { + return arm_emit_thumb_insn (buf, (THUMB_STR + | ENCODE (operand.imm, 5, 6) + | ENCODE (rn, 3, 3) + | ENCODE (rt, 3, 0))); + } + else + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } + +} + +/* Helper function to emit data processing register instructions. */ + +static int +arm_emit_arm_data_processing_reg (uint32_t *buf, uint32_t opcode, + uint8_t cond, uint8_t flags, + uint32_t operands) +{ + return arm_emit_arm_insn (buf, (opcode + | ENCODE (cond, 4, 28) + | ENCODE (flags, 1, 20) + /* No shift. */ + | ENCODE (0, 5, 7) + | ENCODE (0, 2, 5) + | operands)); +} + +/* Helper function to emit data processing immediate instructions. */ + +static int +arm_emit_arm_data_processing_imm (uint32_t *buf, uint32_t opcode, uint8_t cond, + uint8_t rn, uint32_t operands) +{ + return arm_emit_arm_insn (buf, (opcode + | ENCODE (cond, 4, 28) + | ENCODE (rn, 4, 16) + | operands)); +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_arm_add (uint32_t *buf, uint8_t cond, uint8_t flags, uint8_t rd, + uint8_t rn, struct arm_operand operand) +{ + if (operand.type == OPERAND_IMMEDIATE) + { + return arm_emit_arm_insn (buf, (ARM_ADD + /* Immediate operand */ + | ENCODE (1, 1, 25) + | ENCODE (cond, 4, 28) + | ENCODE (flags, 1, 20) + | ENCODE (rn, 4, 16) + | ENCODE (rd, 4, 12) + | ENCODE (operand.imm, 8, 0))); + } + else if (operand.type == OPERAND_REGISTER) + { + return arm_emit_arm_data_processing_reg (buf, ARM_ADD, cond, flags, + (ENCODE (rn, 4, 16) + | ENCODE (rd, 4, 12) + | ENCODE (operand.reg, 4, 0))); + } + else + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_arm_adc (uint32_t *buf, uint8_t cond, uint8_t rd, uint8_t rn, + struct arm_operand operand) +{ + if (operand.type == OPERAND_IMMEDIATE) + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } + else if (operand.type == OPERAND_REGISTER) + { + return arm_emit_arm_data_processing_reg (buf, ARM_ADC, cond, 0, + (ENCODE (rn, 4, 16) + | ENCODE (rd, 4, 12) + | ENCODE (operand.reg, 4, 0))); + } + else + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_thumb_add_sp (uint16_t *buf, struct arm_operand operand) +{ + if (operand.type == OPERAND_IMMEDIATE) + { + return arm_emit_thumb_insn (buf, (THUMB_ADD_SP + | ENCODE (operand.imm >> 2, 7, 0))); + } + else + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_arm_pop_one (uint32_t *buf, uint8_t cond, uint8_t rt) +{ + return arm_emit_arm_insn (buf, (ARM_POP_A2 + | ENCODE (cond, 4, 28) + | ENCODE (rt, 4, 12))); +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_arm_pop_list (uint32_t *buf, uint8_t cond, uint16_t register_list) +{ + return arm_emit_arm_insn (buf, (ARM_POP_A1 + | ENCODE (cond, 4, 28) + | ENCODE (register_list, 16, 0))); +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_thumb_pop (uint16_t *buf, uint8_t register_list, uint8_t pc) +{ + return arm_emit_thumb_insn (buf, (THUMB_POP + | ENCODE (pc, 1, 8) + | ENCODE (register_list, 8, 0))); +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_thumb_popw_list (uint16_t *buf, uint16_t register_list, uint8_t pc, + uint8_t lr) +{ + return arm_emit_thumb_w_insn (buf, (THUMB_POPW + | ENCODE (pc, 1, 15) + | ENCODE (lr, 1, 14) + | ENCODE (register_list, 13, 0))); +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_arm_msr (uint32_t *buf, uint8_t cond, uint8_t rn) +{ + return arm_emit_arm_insn (buf, (ARM_MSR + | ENCODE (cond, 4, 28) + /* Mask 0b11 */ + | ENCODE (3, 2, 18) + | ENCODE (rn, 4, 0))); +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_thumb_msr (uint16_t *buf, uint8_t rn) +{ + return arm_emit_thumb_w_insn (buf, (THUMB_MSR + | ENCODE (rn, 4, 16) + /* Mask 0b11 */ + | ENCODE (3, 2, 10))); +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_arm_vpop (uint32_t *buf, uint8_t cond, uint8_t rs, uint8_t len) +{ + return arm_emit_arm_insn (buf, (ARM_VPOP + | ENCODE (cond, 4, 28) + | ENCODE (bit (rs, 4), 1, 22) + | ENCODE (bits (rs, 0, 3), 4, 12) + | ENCODE (2 * len, 8, 0))); + +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_thumb_vpop (uint16_t *buf, uint8_t rs, uint8_t len) +{ + return arm_emit_thumb_w_insn (buf, (THUMB_VPOP + | ENCODE (bit (rs, 4), 1, 22) + | ENCODE (bits (rs, 0, 3), 4, 12) + | ENCODE (2 * len, 8, 0))); +} + +/* Helper function for LDR and LDRB instructions. */ + +static int +arm_emit_arm_ldr_ldrb (uint32_t *buf, uint32_t opcode, uint8_t cond, uint8_t rt, + uint8_t rn, struct arm_memory_operand operand) +{ + switch (operand.type) + { + case MEMORY_OPERAND_OFFSET: + { + /* Offset P 1 , W 0 */ + /* Pre indexed P 1 W 1 */ + /* Post indexed P 0 W1 */ + /* U 1 if offset add */ + /* U 0 if offset sub */ + + /* Offset P1 , W0. */ + uint32_t offset_op = ENCODE (1, 1, 24) | ENCODE (0, 1, 21); + + /* U1 if offset added or omitted, U0 if subtracted. */ + if (operand.index >= 0) + offset_op |= ENCODE (1, 1, 23); + + return arm_emit_arm_insn (buf, (opcode + | ENCODE (cond, 4, 28) + | offset_op + | ENCODE (rn, 4, 16) + | ENCODE (rt, 4, 12) + | ENCODE (ABS (operand.index), 12, 0))); + } + default: + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } + } +} +/* See arm-insn-emit.h. */ + +int +arm_emit_arm_ldr (uint32_t *buf, uint8_t cond, uint8_t rt, uint8_t rn, + struct arm_memory_operand operand) +{ + return arm_emit_arm_ldr_ldrb (buf, ARM_LDR, cond, rt, rn, operand); +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_arm_ldrb (uint32_t *buf, uint8_t cond, uint8_t rt, uint8_t rn, + struct arm_memory_operand operand) +{ + return arm_emit_arm_ldr_ldrb (buf, ARM_LDRB, cond, rt, rn, operand); +} + + +/* See arm-insn-emit.h. */ + +int +arm_emit_arm_ldrh (uint32_t *buf, uint8_t cond, uint8_t rt, uint8_t rn, + struct arm_memory_operand operand) +{ + switch (operand.type) + { + case MEMORY_OPERAND_OFFSET: + { + /* Offset P 1 , W 0 */ + /* Pre indexed P 1 W 1 */ + /* Post indexed P 0 W1 */ + /* U 1 if offset add */ + /* U 0 if offset sub */ + + /* Offset P1 , W0. */ + uint32_t offset_op = ENCODE (1, 1, 24) | ENCODE (0, 1, 21); + + /* U1 if offset added or omitted, U0 if subtracted. */ + if (operand.index >= 0) + offset_op |= ENCODE (1, 1, 23); + + return arm_emit_arm_insn (buf, + (ARM_LDRH + | ENCODE (cond, 4, 28) + | offset_op + | ENCODE (rn, 4, 16) + | ENCODE (rt, 4, 12) + | ENCODE (bits(ABS (operand.index), 4, 7), + 4, 8) + | ENCODE (bits(ABS (operand.index), 0, 3), + 4, 0))); + } + default: + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } + } +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_arm_ldrd (uint32_t *buf, uint8_t cond, uint8_t rt, uint8_t rn, + struct arm_memory_operand operand) +{ + switch (operand.type) + { + case MEMORY_OPERAND_OFFSET: + { + /* Offset P 1 , W 0 */ + /* Pre indexed P 1 W 1 */ + /* Post indexed P 0 W1 */ + /* U 1 if offset add */ + /* U 0 if offset sub */ + + /* Offset P1 , W0. */ + uint32_t offset_op = ENCODE (1, 1, 24) | ENCODE (0, 1, 21); + + /* U1 if offset added or omitted, U0 if subtracted. */ + if (operand.index >= 0) + offset_op |= ENCODE (1, 1, 23); + + return arm_emit_arm_insn (buf, + (ARM_LDRD + | ENCODE (cond, 4, 28) + | offset_op + | ENCODE (rn, 4, 16) + | ENCODE (rt, 4, 12) + | ENCODE (bits(ABS (operand.index), 4, 7), + 4, 8) + | ENCODE (bits(ABS (operand.index), 0, 3), + 4, 0))); + } + default: + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } + } +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_arm_sbfx (uint32_t *buf, uint8_t cond, uint8_t rd, uint8_t rn, + uint32_t lsb, uint32_t width) +{ + return arm_emit_arm_insn (buf, (ARM_SBFX + | ENCODE (cond, 4, 28) + | ENCODE ((width - 1), 5, 16) + | ENCODE (rd, 4, 12) + | ENCODE (lsb, 5, 7) + | ENCODE (rn, 4, 0))); +} + +/* See arm-insn-emit.h. */ + +int +arm_emit_arm_nop (uint32_t *buf, uint8_t cond) +{ + return arm_emit_arm_insn (buf, ARM_NOP | ENCODE (cond, 4, 28)); +} + +/* See arm-insn-emit.h */ + +int +arm_emit_arm_ubfx (uint32_t *buf, uint8_t cond, uint8_t rd, uint8_t rn, + uint8_t lsb, uint8_t width) +{ + return arm_emit_arm_insn (buf, (ARM_UBFX + | ENCODE (cond, 4, 28) + | ENCODE ((lsb + width - 1), 5, 16) + | ENCODE (rd, 4, 12) + | ENCODE (lsb, 5, 7) + | ENCODE (rn, 4, 0))); +} + +/* See arm-insn-emit.h */ + +int +arm_emit_arm_sub (uint32_t *buf, uint8_t cond, uint8_t flags, + uint8_t rd, uint8_t rn, struct arm_operand operand) +{ + if (operand.type == OPERAND_REGISTER) + { + return arm_emit_arm_data_processing_reg (buf, ARM_SUB, cond, flags, + (ENCODE (rn, 4, 16) + | ENCODE (rd, 4, 12) + | ENCODE (operand.reg, 4, 0))); + } + else if (operand.type == OPERAND_IMMEDIATE) + { + return arm_emit_arm_data_processing_imm (buf, + (ARM_SUB + /* Immediate. */ + | ENCODE (1, 1, 25) + | ENCODE (flags, 1, 20)), + cond, rn, + (ENCODE (rd, 4, 12) + | ENCODE (operand.imm, 12, 0))); + } + else + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } +} + +/* See arm-insn-emit.h */ + +int +arm_emit_arm_rsb (uint32_t *buf, uint8_t cond, uint8_t rd, uint8_t rn, + struct arm_operand operand) +{ + if (operand.type == OPERAND_REGISTER) + { + return arm_emit_arm_data_processing_reg (buf, ARM_RSB, cond, 0, + (ENCODE (rn, 4, 16) + | ENCODE (rd, 4, 12) + | ENCODE (operand.reg, 4, 0))); + } + else if (operand.type == OPERAND_IMMEDIATE) + { + return arm_emit_arm_data_processing_imm (buf, + (ARM_RSB + /* Immediate. */ + | ENCODE (1, 1, 25) + | ENCODE (0, 1, 20)), + cond, rn, + (ENCODE (rd, 4, 12) + | ENCODE (operand.imm, 12, 0))); + } + else + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } +} + +/* See arm-insn-emit.h */ + +int +arm_emit_arm_sbc (uint32_t *buf, uint8_t cond, uint8_t flags, uint8_t rd, + uint8_t rn, struct arm_operand operand) +{ + if (operand.type == OPERAND_REGISTER) + { + return arm_emit_arm_data_processing_reg (buf, ARM_SBC, cond, flags, + (ENCODE (rn, 4, 16) + | ENCODE (rd, 4, 12) + | ENCODE (operand.reg, 4, 0))); + } + else + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } +} + +/* See arm-insn-emit.h */ + +int arm_emit_arm_mul (uint32_t *buf, uint8_t cond, uint8_t rd, uint8_t rn, + struct arm_operand operand) +{ + if (operand.type == OPERAND_REGISTER) + { + return arm_emit_arm_data_processing_reg (buf, ARM_MUL, cond, 0, + (ENCODE (rd, 4, 16) + | ENCODE (operand.reg, 4, 8) + | ENCODE (rn, 4, 0))); + } + else + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } +} + +/* See arm-insn-emit.h */ + +int +arm_emit_arm_umull (uint32_t *buf, uint8_t cond, uint8_t rdlo, uint8_t rdhi, + uint8_t rn, uint8_t rm) +{ + return arm_emit_arm_data_processing_reg (buf, ARM_UMULL, cond, 0, + (ENCODE (rdhi, 4, 16) + | ENCODE (rdlo, 4, 12) + | ENCODE (rm, 4, 8) + | ENCODE (rn, 4, 0))); +} + +/* See arm-insn-emit.h */ + +int +arm_emit_arm_lsl (uint32_t *buf, uint8_t cond, uint8_t rd, uint8_t rn, + struct arm_operand operand) +{ + if (operand.type == OPERAND_REGISTER) + { + + return arm_emit_arm_data_processing_reg (buf, ARM_LSL, cond, 0, + (ENCODE (rd, 4, 12) + | ENCODE (operand.reg, 4, 8) + | ENCODE (rn, 4, 0))); + } + else + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } +} + +/* See arm-insn-emit.h */ + +int +arm_emit_arm_lsr (uint32_t *buf, uint8_t cond, uint8_t rd, uint8_t rn, + struct arm_operand operand) +{ + if (operand.type == OPERAND_REGISTER) + { + + return arm_emit_arm_data_processing_reg (buf, ARM_LSR, cond, 0, + (ENCODE (rd, 4, 12) + | ENCODE (operand.reg, 4, 8) + | ENCODE (rn, 4, 0))); + } + else + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } +} + +/* See arm-insn-emit.h */ + +int +arm_emit_arm_asr (uint32_t *buf, uint8_t cond, uint8_t rd, uint8_t rn, + struct arm_operand operand) +{ + if (operand.type == OPERAND_REGISTER) + { + return arm_emit_arm_data_processing_reg (buf, ARM_ASR, cond, 0, + (ENCODE (rd, 4, 12) + | ENCODE (operand.reg, 4, 8) + /* Register op. */ + | ENCODE (1, 1, 4) + | ENCODE (rn, 4, 0))); + } + else if (operand.type == OPERAND_IMMEDIATE) + { + return arm_emit_arm_data_processing_imm (buf, + (ARM_ASR + | ENCODE (0, 1, 20) + | ENCODE (rd, 4, 12)), + cond, 0, + (ENCODE (operand.imm, 5, 7) + | ENCODE (rn, 4, 0))); + } + else + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } +} + +/* See arm-insn-emit.h */ + +int +arm_emit_arm_and (uint32_t *buf, uint8_t cond, uint8_t rd, uint8_t rn, + struct arm_operand operand) +{ + if (operand.type == OPERAND_REGISTER) + { + return arm_emit_arm_data_processing_reg (buf, ARM_AND, cond, 0, + (ENCODE (rn, 4, 16) + | ENCODE (rd, 4, 12) + | ENCODE (operand.reg, 4, 0))); + } + else + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } +} + +/* See arm-insn-emit.h */ + +int +arm_emit_arm_orr (uint32_t *buf, uint8_t cond, uint8_t rd, uint8_t rn, + struct arm_operand operand) +{ + if (operand.type == OPERAND_REGISTER) + { + return arm_emit_arm_data_processing_reg (buf, ARM_ORR, cond, 0, + (ENCODE (rn, 4, 16) + | ENCODE (rd, 4, 12) + | ENCODE (operand.reg, 4, 0))); + } + else + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } +} + +/* See arm-insn-emit.h */ +int arm_emit_arm_orr_reg_shifted (uint32_t *buf, uint8_t cond, uint8_t rd, + uint8_t rn, uint8_t rm, uint8_t shift, + uint8_t rs) +{ + return arm_emit_arm_insn (buf, (ARM_ORR + | ENCODE (cond, 4, 28) + | ENCODE (0, 1, 20) + | ENCODE (rn, 4, 16) + | ENCODE (rd, 4, 12) + | ENCODE (rs, 4, 8) + | ENCODE (shift, 2, 5) + | ENCODE (1, 1, 4) + | ENCODE (rm, 4, 0))); +} + +/* See arm-insn-emit.h */ + +int +arm_emit_arm_eor (uint32_t *buf, uint8_t cond, uint8_t rd, uint8_t rn, + struct arm_operand operand) +{ + if (operand.type == OPERAND_REGISTER) + { + return arm_emit_arm_data_processing_reg (buf, ARM_EOR, cond, 0, + (ENCODE (rn, 4, 16) + | ENCODE (rd, 4, 12) + | ENCODE (operand.reg, 4, 0))); + } + else + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } +} + +/* See arm-insn-emit.h */ + +int +arm_emit_arm_mvn (uint32_t *buf, uint8_t cond, uint8_t rd, + struct arm_operand operand) +{ + if (operand.type == OPERAND_REGISTER) + { + return arm_emit_arm_data_processing_reg (buf, ARM_MVN, cond, 0, + (ENCODE (rd, 4, 12) + | ENCODE (operand.reg, 4, 0))); + } + else + { + throw_error (NOT_SUPPORTED_ERROR, + _("Unimplemented instruction encoding.")); + } +} diff --git a/gdb/arch/arm-insn-emit.h b/gdb/arch/arm-insn-emit.h new file mode 100644 index 0000000..bcb216d --- /dev/null +++ b/gdb/arch/arm-insn-emit.h @@ -0,0 +1,1087 @@ +/* Copyright (C) 2016 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef ARM_INSN_H +#define ARM_INSN_H 1 + +/* List of the ARM opcodes we need. */ + +enum arm_opcodes + { + ARM_ADC = 0x00A00000, + ARM_ADD = 0x00800000, + ARM_AND = 0x00000000, + ARM_ASR = 0x01A00040, + ARM_B = 0x0A000000, + ARM_BIC = 0x03C00000, + ARM_BLX = 0x012FFF30, + ARM_CMP = 0x01500000, + ARM_DMB = 0xF57FF050, + ARM_EOR = 0x00200000, + ARM_LDR = 0x04100000, + ARM_LDRB = 0x04500000, + ARM_LDRH = 0x005000B0, + ARM_LDRD = 0x004000D0, + ARM_LDREX = 0x01900F9F, + ARM_LSL = 0x01A00010, + ARM_LSR = 0x01A00030, + ARM_MOV = 0x01A00000, + ARM_MOVT = 0x03400000, + ARM_MOVW = 0x03000000, + ARM_MRS = 0x010F0000, + ARM_MSR = 0x0120F000, + ARM_MUL = 0x00000090, + ARM_MVN = 0x01E00000, + ARM_NOP = 0x0320F000, + ARM_ORR = 0x01800000, + ARM_POP_A1 = 0x08BD0000, + ARM_POP_A2 = 0x049D0004, + ARM_PUSH_A1 = 0x092D0000, + ARM_PUSH_A2 = 0x052D0004, + ARM_RSB = 0x00600000, + ARM_SBC = 0x00C00000, + ARM_SBFX = 0x07A00050, + ARM_STR = 0x04000000, + ARM_STREX = 0x01800F90, + ARM_SUB = 0x00400000, + ARM_UBFX = 0x07E00050, + ARM_UMULL = 0x00800090, + ARM_VPOP = 0x0CBD0B00, + ARM_VPUSH = 0x0D2D0B00, + }; + +/* List of the Thumb opcodes we need. */ + +enum thumb_opcodes + { + THUMB_ADD_SP = 0xB000, + THUMB_BLX = 0x4780, + THUMB_B = 0xD000, + THUMB_BIC = 0xF0200000, + THUMB_BW = 0xF0008000, + THUMB_CMP = 0x2800, + THUMB_CMPW = 0xF1B00F00, + THUMB_DMB = 0xF3BF8F50, + THUMB_LDR = 0xF1A00000, + THUMB_LDREX = 0xE8500F00, + THUMB_MOVT = 0xF2C00000, + THUMB_MOVW = 0xF2400000, + THUMB_MOV = 0x4600, + THUMB_MRS = 0xF3EF8000, + THUMB_MSR = 0xF3808000, + THUMB_POP = 0xBC00, + THUMB_POPW = 0xE8BD0000, + THUMB_PUSH_T1 = 0xB400, + THUMB_PUSH_T2 = 0xE92D0000, + THUMB_SBFX = 0XF3400000, + THUMB_STR = 0x6000, + THUMB_STREX = 0xE8400000, + THUMB_VPOP = 0xECBD0B00, + THUMB_VPUSH = 0xED2D0B00, + }; + +struct arm_operand +{ + /* Type of the operand. */ + enum arm_operand_type type; + + /* Value of the operand according to the type. */ + union + { + uint32_t imm; + uint8_t reg; + struct arm_memory_operand mem; + }; +}; + +enum arm_shifts + { + LSL = 0x0, /* Logical shift left. */ + LSR = 0x1, /* Logical shift right. */ + ASR = 0x2, /* Arithmetic shift right. */ + }; + +/* Helper function to create an immediate operand + + Example: + + p += emit_mov (p, r0, immediate_operand (12)); */ + +struct arm_operand immediate_operand (uint32_t imm); + +/* Helper function to create a register operand. + + Example: + + p += emit_mov (p, r0, register_operand (r1)); */ + +struct arm_operand register_operand (uint8_t reg); + +/* Helper function to create an memory operand. + + Example: + + p += emit_mov (p, r0, immediate_operand (12)); */ + +struct arm_operand memory_operand (struct arm_memory_operand mem); + +/* Encode a register list bitfield, using a consecutive list of bits from + FROM with length LENGTH and using an inital value of INITIAL. Max 16 + bits. */ + +uint16_t encode_register_list (uint8_t from, uint8_t length, uint16_t initial); + +/* Return the distance in bytes from FROM to TO and adjusted for prefetch. */ + +uint32_t arm_arm_branch_relative_distance (CORE_ADDR from, CORE_ADDR to); + +/* Return the distance in bytes from FROM to TO and adjusted for prefetch. */ + +uint32_t arm_thumb_branch_relative_distance (CORE_ADDR from, CORE_ADDR to); + +/* Return the distance in bytes from FROM to TO , adjusted for prefetch + and for alignement when switching from ARM mode to Thumb mode. */ + +uint32_t arm_thumb_to_arm_branch_relative_distance (CORE_ADDR from, + CORE_ADDR to); + +/* Return the offset is bytes adjusted for prefetch. */ + +uint32_t arm_arm_branch_adjusted_offset (uint32_t offset); + +/* Return the offset in bytes adjusted for prefetch. */ + +uint32_t arm_thumb_branch_adjusted_offset (uint32_t offset); + +/* Return whether it's possible to jump from FROM to TO using a relative + branch in arm mode. */ + +int arm_arm_is_reachable (CORE_ADDR from, CORE_ADDR to); + +/* Return whether it's possible to jump from FROM to TO using a relative + branch in thumb mode. */ + +int arm_thumb_is_reachable (CORE_ADDR from, CORE_ADDR to); + +/* Write arm instructions to move the 32bit value VAL into register REG. */ + +int arm_emit_arm_mov_32 (uint32_t *mem, int reg, uint32_t val); + +/* Write thumb instructions to move the 32bit value VAL into register REG. */ + +int arm_emit_thumb_mov_32 (uint16_t *mem, int reg, uint32_t val); + +/* Write an ARM B instruction into *BUF. + + Encoding A1 + ARMv4*, ARMv5T*, ARMv6*, ARMv7 + B