1465 lines
37 KiB
C
1465 lines
37 KiB
C
// SPDX-FileCopyrightText: 2015-2018 pancake <pancake@nopcode.org>
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <stdlib.h>
|
|
#include <rz_util.h>
|
|
|
|
typedef enum optype_t {
|
|
ARM_NOTYPE = -1,
|
|
ARM_GPR = 1,
|
|
ARM_CONSTANT = 2,
|
|
ARM_FP = 4,
|
|
ARM_MEM_OPT = 8,
|
|
ARM_SHIFT = 16,
|
|
ARM_EXTEND = 32
|
|
} OpType;
|
|
|
|
typedef enum regtype_t {
|
|
ARM_UNDEFINED = -1,
|
|
ARM_REG64 = 1,
|
|
ARM_REG32 = 2,
|
|
ARM_SP = 4,
|
|
ARM_LR = 8,
|
|
ARM_PC = 16,
|
|
ARM_SIMD = 32
|
|
} RegType;
|
|
|
|
typedef enum shifttype_t {
|
|
ARM_LSL = 0,
|
|
ARM_LSR = 1,
|
|
ARM_ASR = 2,
|
|
ARM_ROR = 3,
|
|
ARM_UXTB,
|
|
ARM_UXTH,
|
|
ARM_UXTW,
|
|
ARM_UXTX,
|
|
ARM_SXTB,
|
|
ARM_SXTH,
|
|
ARM_SXTW,
|
|
ARM_SXTX
|
|
} ShiftType;
|
|
|
|
typedef enum logicalop_t {
|
|
ARM_AND = 0,
|
|
ARM_ORR = 1,
|
|
ARM_EOR = 2,
|
|
ARM_ANDS = 3
|
|
} LogicalOp;
|
|
|
|
typedef struct operand_t {
|
|
OpType type;
|
|
union {
|
|
struct {
|
|
int reg;
|
|
RegType reg_type;
|
|
ut16 sp_val;
|
|
};
|
|
struct {
|
|
ut64 immediate;
|
|
bool sign;
|
|
bool preindex;
|
|
};
|
|
struct {
|
|
ut64 shift_amount;
|
|
ShiftType shift;
|
|
bool amount_present;
|
|
};
|
|
struct {
|
|
ut32 mem_option;
|
|
};
|
|
};
|
|
} Operand;
|
|
|
|
#define MAX_OPERANDS 7
|
|
|
|
typedef struct Opcode_t {
|
|
char *mnemonic;
|
|
ut32 op[3];
|
|
size_t op_len;
|
|
ut8 opcode[3];
|
|
bool writeback;
|
|
int operands_count;
|
|
Operand operands[MAX_OPERANDS];
|
|
} ArmOp;
|
|
|
|
#define check_cond(cond) \
|
|
if (!(cond)) { \
|
|
return data; \
|
|
}
|
|
|
|
static int get_mem_option(char *token) {
|
|
// values 4, 8, 12, are unused. XXX to adjust
|
|
const char *options[] = { "sy", "st", "ld", "xxx", "ish", "ishst",
|
|
"ishld", "xxx", "nsh", "nshst", "nshld",
|
|
"xxx", "osh", "oshst", "oshld", NULL };
|
|
int i = 0;
|
|
while (options[i]) {
|
|
if (!rz_str_casecmp(token, options[i])) {
|
|
return 15 - i;
|
|
}
|
|
i++;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static int countLeadingZeros(ut64 x) {
|
|
int count = 64;
|
|
while (x) {
|
|
x >>= 1;
|
|
--count;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static int countTrailingZeros(ut64 x) {
|
|
int count = 0;
|
|
while (x && !(x & 1)) {
|
|
count++;
|
|
x >>= 1;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static int calcNegOffset(int n, int shift) {
|
|
int a = n >> shift;
|
|
if (a == 0) {
|
|
return 0xff;
|
|
}
|
|
// find first set bit then invert it and all
|
|
// bits below it
|
|
int t = 0x400;
|
|
while (!(t & a) && a != 0 && t != 0) {
|
|
t = t >> 1;
|
|
}
|
|
t = t & (t - 1);
|
|
a = a ^ t;
|
|
// If bits below 32 are set
|
|
if (countTrailingZeros(n) > shift) {
|
|
a--;
|
|
}
|
|
return 0xff & (0xff - a);
|
|
}
|
|
|
|
static int countLeadingOnes(ut64 x) {
|
|
return countLeadingZeros(~x);
|
|
}
|
|
|
|
static int countTrailingOnes(ut64 x) {
|
|
return countTrailingZeros(~x);
|
|
}
|
|
|
|
static bool isMask(ut64 value) {
|
|
return value && ((value + 1) & value) == 0;
|
|
}
|
|
|
|
static bool isShiftedMask(ut64 value) {
|
|
return value && isMask((value - 1) | value);
|
|
}
|
|
|
|
// https://llvm.org/doxygen/AArch64AddressingModes_8h_source.html
|
|
static ut32 encodeBitMasksWithSize(ut64 imm, ut32 reg_size) {
|
|
if (imm == 0 || imm == UT64_MAX || (reg_size != 64 && (imm >> reg_size != 0 || imm == (~0ULL >> (64 - reg_size))))) {
|
|
return UT32_MAX;
|
|
}
|
|
// get element size
|
|
ut32 size = reg_size;
|
|
do {
|
|
size >>= 1;
|
|
ut64 mask = (1ull << size) - 1;
|
|
if ((imm & mask) != ((imm >> size) & mask)) {
|
|
size <<= 1;
|
|
break;
|
|
}
|
|
} while (size > 2);
|
|
// determine rot to make element be 0^m 1^n
|
|
ut32 cto, i;
|
|
ut64 mask = UT64_MAX >> (64 - size);
|
|
imm &= mask;
|
|
|
|
if (isShiftedMask(imm)) {
|
|
i = countTrailingZeros(imm);
|
|
cto = countTrailingOnes(imm >> i);
|
|
} else {
|
|
imm |= ~mask;
|
|
if (!isShiftedMask(~imm)) {
|
|
return UT32_MAX;
|
|
}
|
|
|
|
ut32 clo = countLeadingOnes(imm);
|
|
i = 64 - clo;
|
|
cto = clo + countTrailingOnes(imm) - (64 - size);
|
|
}
|
|
|
|
// Encode in Immr the number of RORs it would take to get *from* 0^m 1^n
|
|
// to our target value, where I is the number of RORs to go the opposite
|
|
// direction
|
|
ut32 immr = (size - i) & (size - 1);
|
|
// If size has a 1 in the n'th bit, create a value that has zeroes in
|
|
// bits [0, n] and ones above that.
|
|
ut64 nimms = ~(size - 1ULL) << 1;
|
|
// Or the cto value into the low bits, which must be below the Nth bit
|
|
// bit mentioned above.
|
|
nimms |= (cto - 1);
|
|
// Extract and toggle seventh bit to make N field.
|
|
ut32 n = ((nimms >> 6) & 1) ^ 1;
|
|
ut32 encoding = (n << 12) | (immr << 6) | (nimms & 0x3f);
|
|
return encoding;
|
|
}
|
|
|
|
static inline ut32 encode1reg(ArmOp *op) {
|
|
return op->operands[0].reg << 24;
|
|
}
|
|
|
|
static inline ut32 encode2regs(ArmOp *op) {
|
|
return (op->operands[1].reg & 0x7) << 29 | (op->operands[1].reg & 0x18) << 13 | encode1reg(op);
|
|
}
|
|
|
|
static inline ut32 encodeImm9(ut32 n) {
|
|
return (n & 0x1f0) << 4 | (n & 0xf) << 20;
|
|
}
|
|
|
|
static ut32 mov(ArmOp *op) {
|
|
ut32 k = 0;
|
|
ut32 data = UT32_MAX;
|
|
check_cond(op->operands_count >= 2);
|
|
check_cond(op->operands[0].type == ARM_GPR);
|
|
int bits = (op->operands[0].reg_type & ARM_REG64) ? 64 : 32;
|
|
if (bits == 64) {
|
|
k = 0x0080;
|
|
}
|
|
k |= encode1reg(op);
|
|
if (!strcmp(op->mnemonic, "mov")) {
|
|
check_cond(op->operands_count == 2);
|
|
if (op->operands[1].type == ARM_GPR) {
|
|
check_cond(bits == ((op->operands[1].reg_type & ARM_REG64) ? 64 : 32));
|
|
if (op->operands[0].reg_type & ARM_SP || op->operands[1].reg_type & ARM_SP) { // alias of add
|
|
k |= 0x0011;
|
|
data = k | encode2regs(op);
|
|
return data;
|
|
}
|
|
k |= 0xe003002a; // alias of orr
|
|
data = k | op->operands[1].reg << 8;
|
|
return data;
|
|
}
|
|
check_cond(op->operands[1].type & ARM_CONSTANT);
|
|
ut64 imm = op->operands[1].immediate;
|
|
ut64 imm_inverse = ~imm;
|
|
if (bits == 32) {
|
|
check_cond(imm <= 0xffffffff || imm_inverse <= 0xffffffff);
|
|
imm &= 0xffffffff;
|
|
imm_inverse &= 0xffffffff;
|
|
}
|
|
int shift;
|
|
ut64 mask = 0xffff;
|
|
for (shift = 0; shift < bits; shift += 16, mask <<= 16) {
|
|
if (imm == (imm & mask)) { // movz
|
|
data = k | 0x00008052;
|
|
imm >>= shift;
|
|
data |= (imm & 7) << 29 | (imm & 0x7f8) << 13 | (imm & 0xf800) >> 3;
|
|
data |= shift << 9;
|
|
return data;
|
|
}
|
|
}
|
|
mask = 0xffff;
|
|
for (shift = 0; shift < bits; shift += 16, mask <<= 16) {
|
|
if (imm_inverse == (imm_inverse & mask)) { // movn
|
|
data = k | 0x00008012;
|
|
imm_inverse >>= shift;
|
|
data |= (imm_inverse & 7) << 29 | (imm_inverse & 0x7f8) << 13 | (imm_inverse & 0xf800) >> 3;
|
|
data |= shift << 9;
|
|
return data;
|
|
}
|
|
}
|
|
ut32 bitmask = encodeBitMasksWithSize(op->operands[1].immediate, bits); // orr
|
|
check_cond(bitmask != UT32_MAX);
|
|
data = k | 0xe0030032;
|
|
data |= (bitmask & 0x3f) << 18 | (bitmask & 0x1fc0) << 2;
|
|
return data;
|
|
}
|
|
if (!strcmp(op->mnemonic, "movz")) {
|
|
k |= 0x8052;
|
|
} else if (!strcmp(op->mnemonic, "movk")) {
|
|
k |= 0x8072;
|
|
} else if (!strcmp(op->mnemonic, "movn")) {
|
|
k |= 0x8012;
|
|
} else {
|
|
return data;
|
|
}
|
|
check_cond(op->operands[1].type == ARM_CONSTANT);
|
|
ut64 imm = op->operands[1].immediate;
|
|
check_cond(imm <= 0xffff);
|
|
int shift = 0;
|
|
if (op->operands_count >= 3) {
|
|
check_cond(op->operands_count == 3);
|
|
check_cond(op->operands[2].type == ARM_SHIFT);
|
|
check_cond(op->operands[2].shift == ARM_LSL);
|
|
shift = op->operands[2].shift_amount;
|
|
check_cond(!(shift & 0xf));
|
|
check_cond(shift < bits);
|
|
}
|
|
data = k;
|
|
data |= (imm & 7) << 29; // arg(1)
|
|
data |= (imm & 0x7f8) << 13; // arg(1)
|
|
data |= (imm & 0xf800) >> 3; // arg(1)
|
|
data |= shift << 9; // arg(2)
|
|
return data;
|
|
}
|
|
|
|
static ut32 cb(ArmOp *op) {
|
|
ut32 data = UT32_MAX;
|
|
int k = 0;
|
|
if (!strncmp(op->mnemonic, "cbnz", 4)) {
|
|
if (op->operands[0].reg_type & ARM_REG64) {
|
|
k = 0x000000b5;
|
|
} else if (op->operands[0].reg_type & ARM_REG32) {
|
|
k = 0x00000035;
|
|
} else {
|
|
return UT32_MAX;
|
|
}
|
|
} else if (!strncmp(op->mnemonic, "cbz", 3)) {
|
|
if (op->operands[0].reg_type & ARM_REG64) {
|
|
k = 0x000000b4;
|
|
} else if (op->operands[0].reg_type & ARM_REG32) {
|
|
k = 0x00000034;
|
|
} else {
|
|
return UT32_MAX;
|
|
}
|
|
} else {
|
|
return UT32_MAX;
|
|
}
|
|
// printf ("%s %d, %llu\n", op->mnemonic, op->operands[0].reg, op->operands[1].immediate);
|
|
ut32 imm = op->operands[1].immediate;
|
|
data = k | encode1reg(op) | ((imm & 0x1c) << 27) | ((imm & 0x1fe0) << 11);
|
|
data = data | ((imm & 0x1fe000) >> 5);
|
|
|
|
return data;
|
|
}
|
|
|
|
static ut32 cmp(ArmOp *op) {
|
|
ut32 data = UT32_MAX;
|
|
int k = 0;
|
|
if (op->operands[0].reg_type & ARM_REG64 && op->operands[1].reg_type & ARM_REG64) {
|
|
k = 0x1f0000eb;
|
|
} else if (op->operands[0].reg_type & ARM_REG32 && op->operands[1].reg_type & ARM_REG32) {
|
|
if (op->operands[2].shift_amount > 31) {
|
|
return UT32_MAX;
|
|
}
|
|
k = 0x1f00006b;
|
|
} else {
|
|
return UT32_MAX;
|
|
}
|
|
|
|
data = k | (op->operands[0].reg & 0x18) << 13 | op->operands[0].reg << 29 | op->operands[1].reg << 8;
|
|
|
|
if (op->operands[2].type != ARM_SHIFT) {
|
|
data |= op->operands[2].shift_amount << 18 | op->operands[2].shift << 14;
|
|
}
|
|
return data;
|
|
}
|
|
|
|
static ut32 regsluop(ArmOp *op, int k) {
|
|
ut32 data = UT32_MAX;
|
|
|
|
if (op->operands[1].reg_type & ARM_REG32) {
|
|
return data;
|
|
}
|
|
if (op->operands[0].reg_type & ARM_REG32) {
|
|
k -= 0x40;
|
|
}
|
|
if (op->operands[2].type & ARM_GPR) {
|
|
return data;
|
|
}
|
|
|
|
int n = op->operands[2].immediate;
|
|
if (n > 0xff || n < -0x100) {
|
|
return data;
|
|
}
|
|
|
|
data = k | encode2regs(op);
|
|
|
|
if (n < 0) {
|
|
n *= -1;
|
|
data |= (0xf & (0xf - (n - 1))) << 20;
|
|
|
|
if (countTrailingZeros(n) > 3) {
|
|
data |= (0x1f - ((n >> 4) - 1)) << 8;
|
|
} else {
|
|
data |= (0x1f - (n >> 4)) << 8;
|
|
}
|
|
} else {
|
|
data |= (0xf & (n & 63)) << 20;
|
|
if (countTrailingZeros(n) < 4) {
|
|
data |= (n >> 4) << 8;
|
|
} else {
|
|
data |= (0xff & n) << 4;
|
|
}
|
|
data |= (n >> 8) << 8;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
// Register Load/store ops
|
|
static ut32 reglsop(ArmOp *op, int k) {
|
|
ut32 data = UT32_MAX;
|
|
|
|
if (op->operands[1].reg_type & ARM_REG32) {
|
|
return data;
|
|
}
|
|
if (op->operands[0].reg_type & ARM_REG32) {
|
|
k -= 0x40;
|
|
}
|
|
if (op->operands[2].type & ARM_GPR) {
|
|
k += 0x00682000;
|
|
data = k | encode2regs(op);
|
|
data |= op->operands[2].reg << 8;
|
|
} else {
|
|
int n = op->operands[2].immediate;
|
|
if (n > 0x100 || n < -0x100) {
|
|
return UT32_MAX;
|
|
}
|
|
|
|
if (n == 0 || (n > 0 && countTrailingZeros(n) >= 4)) {
|
|
k++;
|
|
}
|
|
data = k | encode2regs(op);
|
|
|
|
if (n < 0) {
|
|
n *= -1;
|
|
data |= (0xf & (0xf - (n - 1))) << 20;
|
|
if (countTrailingZeros(n) > 3) {
|
|
data |= (0x1f - ((n >> 4) - 1)) << 8;
|
|
} else {
|
|
data |= (0x1f - (n >> 4)) << 8;
|
|
}
|
|
} else {
|
|
if (op->operands[0].reg_type & ARM_REG32) {
|
|
if (countTrailingZeros(n) < 2) {
|
|
data |= (0xf & (n & 63)) << 20;
|
|
data |= (n >> 4) << 8;
|
|
} else {
|
|
data++;
|
|
data |= (0xff & n) << 16;
|
|
}
|
|
data |= (n >> 8) << 8;
|
|
} else {
|
|
data |= (0xf & (n & 63)) << 20;
|
|
if (countTrailingZeros(n) < 4) {
|
|
data |= (n >> 4) << 8;
|
|
} else {
|
|
data |= (0xff & n) << 15;
|
|
}
|
|
data |= (n >> 8) << 23;
|
|
}
|
|
}
|
|
}
|
|
return data;
|
|
}
|
|
|
|
// load/store ops
|
|
static ut32 lsop(ArmOp *op, int k, ut64 addr) {
|
|
ut32 data = UT32_MAX;
|
|
int op_count = op->operands_count;
|
|
if (k == 0x00000098) { // ldrsw
|
|
if (op->operands[0].type & ARM_GPR && op->operands[1].type & ARM_CONSTANT) { // (literal)
|
|
st64 offset = op->operands[1].immediate - addr;
|
|
check_cond(op->operands[0].reg_type & ARM_REG64);
|
|
check_cond(!(offset & 0x3));
|
|
check_cond(-0x100000 <= offset && offset < 0x100000);
|
|
offset >>= 2;
|
|
data = k | (offset & 0x7f800) >> 3 | (offset & 0x7f8) << 13 | (offset & 0x7) << 29 | encode1reg(op);
|
|
return data;
|
|
}
|
|
k = 0x000080b8;
|
|
}
|
|
check_cond(op->operands[0].type == ARM_GPR);
|
|
check_cond(op->operands[1].type == ARM_GPR);
|
|
check_cond(op->operands[1].reg_type & ARM_REG64);
|
|
k |= encode2regs(op);
|
|
if (!strcmp(op->mnemonic, "ldrb") || !strcmp(op->mnemonic, "ldrh") || !strcmp(op->mnemonic, "strb") || !strcmp(op->mnemonic, "strh")) {
|
|
check_cond(op->operands[0].reg_type & ARM_REG32);
|
|
} else if (!strcmp(op->mnemonic, "ldrsw")) {
|
|
check_cond(op->operands[0].reg_type & ARM_REG64);
|
|
} else { // ldrsh, ldrsb
|
|
if (op->operands[0].reg_type & ARM_REG32) {
|
|
k |= 0x00004000;
|
|
}
|
|
}
|
|
char width = op->mnemonic[strlen(op->mnemonic) - 1];
|
|
if (op->operands[2].type & ARM_GPR) {
|
|
k |= 0x00482000;
|
|
if (op->operands[3].type == ARM_EXTEND) {
|
|
switch (op->operands[3].shift) {
|
|
case ARM_SXTW:
|
|
k |= 0x00800000;
|
|
// fall through
|
|
case ARM_UXTW:
|
|
check_cond(op->operands[2].reg_type & ARM_REG32);
|
|
break;
|
|
case ARM_SXTX:
|
|
k |= 0x00a00000;
|
|
check_cond(op->operands[2].reg_type & ARM_REG64);
|
|
break;
|
|
default:
|
|
return data;
|
|
}
|
|
} else if (op->operands[3].type == ARM_SHIFT) {
|
|
check_cond(op->operands[3].shift == ARM_LSL);
|
|
check_cond(op->operands[2].reg_type & ARM_REG64);
|
|
k |= 0x00200000;
|
|
}
|
|
if (op->operands[3].type == ARM_EXTEND || op->operands[3].type == ARM_SHIFT) {
|
|
if (width == 'b') {
|
|
check_cond(op->operands[3].shift_amount == 0);
|
|
if (op->operands[3].amount_present) {
|
|
k |= 0x00100000;
|
|
}
|
|
} else if (width == 'h') {
|
|
switch (op->operands[3].shift_amount) {
|
|
case 1:
|
|
k |= 0x00100000;
|
|
// fall through
|
|
case 0:
|
|
break;
|
|
default:
|
|
return data;
|
|
}
|
|
} else { // w
|
|
switch (op->operands[3].shift_amount) {
|
|
case 2:
|
|
k |= 0x00100000;
|
|
// fall through
|
|
case 0:
|
|
break;
|
|
default:
|
|
return data;
|
|
}
|
|
}
|
|
} else { // lsl 0 by default
|
|
check_cond(op->operands[2].reg_type & ARM_REG64);
|
|
k |= 0x00200000;
|
|
}
|
|
data = k | op->operands[2].reg << 8;
|
|
return data;
|
|
}
|
|
check_cond(op_count == 2 || op->operands[2].type == ARM_CONSTANT);
|
|
check_cond(!op->writeback || op->operands[2].preindex);
|
|
int n = op_count == 2 ? 0 : op->operands[2].immediate;
|
|
if (!op->writeback && (op_count == 2 || op->operands[2].preindex)) { // unsigned offset
|
|
check_cond(n >= 0);
|
|
if (width == 'b') {
|
|
check_cond(n <= 0xfff);
|
|
} else if (width == 'h') {
|
|
check_cond(n <= 0x1ffe && !(n & 1))
|
|
n >>= 1;
|
|
} else { // w
|
|
check_cond(n <= 0x3ffc && !(n & 3));
|
|
n >>= 2;
|
|
}
|
|
data = k | (n & 0x3f) << 18 | (n & 0xfc0) << 2 | 1;
|
|
return data;
|
|
}
|
|
check_cond(-0x100 <= n && n < 0x100) if (op->operands[2].preindex) {
|
|
k |= 0x00080000;
|
|
}
|
|
data = k | encodeImm9(n) | 0x00040000;
|
|
return data;
|
|
}
|
|
|
|
static ut32 branch(ArmOp *op, ut64 addr, int k) {
|
|
ut32 data = UT32_MAX;
|
|
ut64 n = 0;
|
|
if (op->operands[0].type & ARM_CONSTANT) {
|
|
n = op->operands[0].immediate;
|
|
if (!(n & 0x3)) {
|
|
if (n >= addr) {
|
|
n -= addr;
|
|
} else {
|
|
n -= addr;
|
|
n = n & 0xfffffff;
|
|
k |= 3;
|
|
}
|
|
n = n >> 2;
|
|
int t = (n & 0xff000000) >> 24;
|
|
int h = (n & 0xff0000) >> 16;
|
|
int m = (n & 0xff00) >> 8;
|
|
n &= 0xff;
|
|
data = k;
|
|
data |= n << 24;
|
|
data |= m << 16;
|
|
data |= h << 8;
|
|
data |= t;
|
|
}
|
|
} else {
|
|
n = op->operands[0].reg;
|
|
if (n >= 31) {
|
|
return -1;
|
|
}
|
|
n = n << 5;
|
|
int h = n >> 8;
|
|
n &= 0xff;
|
|
data = k;
|
|
data |= n << 24;
|
|
data |= h << 16;
|
|
}
|
|
return data;
|
|
}
|
|
|
|
static ut32 bdot(ArmOp *op, ut64 addr, int k) {
|
|
ut32 data = UT32_MAX;
|
|
int n = 0;
|
|
int a = 0;
|
|
n = op->operands[0].immediate;
|
|
// I am sure there's a logical way to do negative offsets,
|
|
// but I was unable to find any sensible docs so I did my best
|
|
if (!(n & 0x3 || n > 0x7ffffff)) {
|
|
n -= addr;
|
|
data = k;
|
|
if (n < 0) {
|
|
n *= -1;
|
|
a = (n << 3) - 1;
|
|
data |= (0xff - a) << 24;
|
|
|
|
a = calcNegOffset(n, 5);
|
|
data |= a << 16;
|
|
|
|
a = calcNegOffset(n, 13);
|
|
data |= a << 8;
|
|
} else {
|
|
data |= (n & 31) << 27;
|
|
data |= (0xff & (n >> 5)) << 16;
|
|
data |= (0xff & (n >> 13)) << 8;
|
|
}
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
/**
|
|
* Determine the 4-bit condition code for given string representation (e.g. "eq", "ne", ...)
|
|
* \p str string of at least 2 chars
|
|
* \return the 4-bit condition or UT8_MAX if invalid
|
|
*/
|
|
static ut8 parse_cond(const char *str) {
|
|
// clang-format off
|
|
switch (((ut16)str[0] << 8 | (ut16)str[1])) {
|
|
#define COND_CASE(a, b) case (ut16)(a) << 8 | (ut16)(b)
|
|
COND_CASE('e', 'q'): return 0x0;
|
|
COND_CASE('n', 'e'): return 0x1;
|
|
COND_CASE('c', 's'): return 0x2;
|
|
COND_CASE('h', 's'): return 0x2;
|
|
COND_CASE('c', 'c'): return 0x3;
|
|
COND_CASE('l', 'o'): return 0x3;
|
|
COND_CASE('m', 'i'): return 0x4;
|
|
COND_CASE('p', 'l'): return 0x5;
|
|
COND_CASE('v', 's'): return 0x6;
|
|
COND_CASE('v', 'c'): return 0x7;
|
|
COND_CASE('h', 'i'): return 0x8;
|
|
COND_CASE('l', 's'): return 0x9;
|
|
COND_CASE('g', 'e'): return 0xa;
|
|
COND_CASE('l', 't'): return 0xb;
|
|
COND_CASE('g', 't'): return 0xc;
|
|
COND_CASE('l', 'e'): return 0xd;
|
|
COND_CASE('a', 'l'): return 0xe;
|
|
COND_CASE('n', 'v'): return 0xf;
|
|
default: return UT8_MAX;
|
|
#undef COND_CASE
|
|
}
|
|
// clang-format on
|
|
}
|
|
|
|
static ut32 parse_bdot(const char *str, ArmOp *op, ut64 addr) {
|
|
if (!str[0] || !str[1] || str[2] != ' ') {
|
|
inval:
|
|
RZ_LOG_ERROR("assembler: arm64: invalid condition\n");
|
|
return UT32_MAX;
|
|
}
|
|
ut8 cond = parse_cond(str);
|
|
if (cond == UT8_MAX) {
|
|
goto inval;
|
|
}
|
|
return bdot(op, addr, 0x00000054 | ((ut32)cond << 24));
|
|
}
|
|
|
|
static ut32 mem_barrier(ArmOp *op, ut64 addr, int k) {
|
|
ut32 data = UT32_MAX;
|
|
data = k;
|
|
if (!strncmp(op->mnemonic, "isb", 3)) {
|
|
if (op->operands[0].mem_option == 15 || op->operands[0].type == ARM_NOTYPE) {
|
|
return data;
|
|
} else {
|
|
return UT32_MAX;
|
|
}
|
|
}
|
|
if (op->operands[0].type == ARM_MEM_OPT) {
|
|
data |= op->operands[0].mem_option << 16;
|
|
} else if (op->operands_count == 1 && op->operands[0].type == ARM_CONSTANT) {
|
|
data |= (op->operands[0].immediate << 16);
|
|
}
|
|
return data;
|
|
}
|
|
|
|
#include "armass64_const.h"
|
|
|
|
static ut32 msr(ArmOp *op, int w) {
|
|
ut32 data = UT32_MAX;
|
|
ut32 seq_data = UT32_MAX;
|
|
int is_immediate = 0;
|
|
int i;
|
|
ut32 r, b;
|
|
/* handle swapped args */
|
|
if (w) {
|
|
if (op->operands[1].reg_type != (ARM_REG64 | ARM_SP)) {
|
|
if (op->operands[1].type == ARM_CONSTANT) {
|
|
for (i = 0; msr_const[i].name; i++) {
|
|
if (op->operands[1].immediate == msr_const[i].val) {
|
|
op->operands[1].sp_val = msr_const[i].val;
|
|
op->operands[1].reg = op->operands[1].immediate;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
return data;
|
|
}
|
|
}
|
|
r = op->operands[0].reg;
|
|
b = op->operands[1].sp_val;
|
|
} else {
|
|
if (op->operands[0].reg_type != (ARM_REG64 | ARM_SP)) {
|
|
if (op->operands[0].type == ARM_CONSTANT) {
|
|
for (i = 0; msr_const[i].name; i++) {
|
|
if (op->operands[0].immediate == msr_const[i].val) {
|
|
op->operands[0].sp_val = msr_const[i].val;
|
|
op->operands[0].reg = op->operands[0].immediate;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
return data;
|
|
}
|
|
}
|
|
r = op->operands[1].reg;
|
|
if (op->operands[1].sp_val == 0xfffe) {
|
|
is_immediate = 1;
|
|
r = op->operands[1].immediate;
|
|
}
|
|
b = op->operands[0].sp_val;
|
|
}
|
|
data = 0x00000000;
|
|
|
|
if (is_immediate) {
|
|
// only msr has immediate mode
|
|
data = 0xd500401f;
|
|
if (b == 0xc210) { // op0 is SPSel
|
|
b = 0x05; // set to immediate mode encoding
|
|
}
|
|
|
|
data |= (b & 0xf0) << 12; // op1
|
|
data |= (b & 0x0f) << 5; // op2
|
|
data |= (r & 0xf) << 8; // CRm(#imm)
|
|
|
|
} else {
|
|
if (w) {
|
|
/* mrs */
|
|
data |= 0xd5200000;
|
|
} else {
|
|
data |= 0xd5000000;
|
|
}
|
|
data |= b << 5;
|
|
data |= r;
|
|
}
|
|
seq_data = 0x00000000;
|
|
seq_data |= (data & 0xff) << 8 * 3;
|
|
seq_data |= (data & 0xff00) << 8 * 1;
|
|
seq_data |= (data & 0xff0000) >> 8 * 1;
|
|
seq_data |= (data & 0xff000000) >> 8 * 3;
|
|
/*
|
|
if (op->operands[1].reg_type == ARM_REG64) {
|
|
data |= op->operands[1].reg << 24;
|
|
}
|
|
*/
|
|
return seq_data;
|
|
}
|
|
|
|
static ut32 logical(ArmOp *op, bool invert, LogicalOp opc) {
|
|
ut32 data = UT32_MAX;
|
|
RegType reg_type = op->operands[0].reg_type;
|
|
|
|
// Reg types need to match
|
|
if (!(reg_type == op->operands[1].reg_type)) {
|
|
return data;
|
|
}
|
|
|
|
OpType op2_type = op->operands[2].type;
|
|
if (op2_type == ARM_CONSTANT) {
|
|
if (invert) {
|
|
/* there aren't inverted immediates in arm64 */
|
|
return UT32_MAX;
|
|
}
|
|
if (reg_type & ARM_REG64) {
|
|
data = 0x92000000;
|
|
} else if (reg_type & ARM_REG32) {
|
|
data = 0x12000000;
|
|
} else {
|
|
return UT32_MAX;
|
|
}
|
|
|
|
bool is64bit = reg_type & ARM_REG64;
|
|
|
|
data |= op->operands[0].reg;
|
|
data |= op->operands[1].reg << 5;
|
|
data |= (opc & 3) << 29;
|
|
|
|
ut32 imm_orig = op->operands[2].immediate;
|
|
ut32 imm_mask = encodeBitMasksWithSize(invert ? ~imm_orig : imm_orig, is64bit ? 64 : 32);
|
|
if (imm_mask == UT32_MAX) {
|
|
return UT32_MAX;
|
|
}
|
|
data |= (imm_mask & 0x1fff) << 10;
|
|
} else if (op2_type == ARM_GPR) {
|
|
if (reg_type & ARM_REG64) {
|
|
data = 0x8a000000;
|
|
} else if (reg_type & ARM_REG32) {
|
|
data = 0x0a000000;
|
|
} else {
|
|
return UT32_MAX;
|
|
}
|
|
|
|
data |= op->operands[0].reg;
|
|
data |= op->operands[1].reg << 5;
|
|
data |= op->operands[2].reg << 16;
|
|
data |= (opc & 3) << 29;
|
|
|
|
if (op->operands_count == 4) {
|
|
Operand shift_op = op->operands[3];
|
|
if (shift_op.type == ARM_SHIFT) {
|
|
data |= (shift_op.shift_amount & 0x3f) << 10;
|
|
data |= (shift_op.shift & 0x3) << 22;
|
|
}
|
|
}
|
|
|
|
if (invert) {
|
|
data |= 1 << 21;
|
|
}
|
|
} else {
|
|
return UT32_MAX;
|
|
}
|
|
return rz_swap_ut32(data);
|
|
}
|
|
|
|
static ut32 adrp(ArmOp *op, ut64 addr) { //, int reg, ut64 dst) {
|
|
ut32 data = 0x90000000;
|
|
if (op->operands[0].type != ARM_GPR) {
|
|
RZ_LOG_ERROR("assembler: arm64: adrp: invalid assembly. valid usage: adrp x0, addr\n");
|
|
return UT32_MAX;
|
|
}
|
|
data |= op->operands[0].reg & 0x1f;
|
|
if (op->operands[1].type != ARM_CONSTANT) {
|
|
RZ_LOG_ERROR("assembler: arm64: adrp: invalid assembly. valid usage: adrp x0, addr\n");
|
|
return UT32_MAX;
|
|
}
|
|
ut64 imm = op->operands[1].immediate & ~0xfff;
|
|
imm -= addr & ~0xfff;
|
|
imm >>= 12;
|
|
data |= (imm & 3) << 29;
|
|
data |= ((imm >> 2) & rz_num_bitmask(19)) << 5;
|
|
return rz_swap_ut32(data);
|
|
}
|
|
|
|
static ut32 adr(ArmOp *op, int addr) {
|
|
ut32 data = UT32_MAX;
|
|
ut64 at = 0LL;
|
|
|
|
if (op->operands[1].type & ARM_CONSTANT) {
|
|
// XXX what about negative values?
|
|
at = op->operands[1].immediate - addr;
|
|
at /= 4;
|
|
}
|
|
data = 0x00000030;
|
|
data |= encode1reg(op);
|
|
ut8 b0 = at;
|
|
ut8 b1 = (at >> 3) & 0xff;
|
|
ut8 b2 = (at >> (8 + 7)) & 0xff;
|
|
data += b0 << 29;
|
|
data += b1 << 16;
|
|
data += b2 << 24;
|
|
return data;
|
|
}
|
|
|
|
static ut32 stp(ArmOp *op, int k) {
|
|
ut32 data = UT32_MAX;
|
|
|
|
if (op->operands[3].immediate & 0x7) {
|
|
return data;
|
|
}
|
|
if (k == 0x000040a9 && (op->operands[0].reg == op->operands[1].reg)) {
|
|
return data;
|
|
}
|
|
|
|
data = k;
|
|
data |= encode2regs(op);
|
|
data += (op->operands[3].immediate & 0x8) << 20;
|
|
data += (op->operands[3].immediate >> 4) << 8;
|
|
return data;
|
|
}
|
|
|
|
static ut32 exception(ArmOp *op, ut32 k) {
|
|
ut32 data = UT32_MAX;
|
|
|
|
if (op->operands[0].type == ARM_CONSTANT) {
|
|
int n = op->operands[0].immediate;
|
|
data = k;
|
|
data += (((n / 8) & 0xff) << 16);
|
|
data += n << 29; //((n >> 8) << 8);
|
|
}
|
|
return data;
|
|
}
|
|
|
|
static ut32 arithmetic(ArmOp *op, int k) {
|
|
ut32 data = UT32_MAX;
|
|
if (op->operands_count < 3) {
|
|
return data;
|
|
}
|
|
|
|
if (!(op->operands[0].type & ARM_GPR &&
|
|
op->operands[1].type & ARM_GPR)) {
|
|
return data;
|
|
}
|
|
if (op->operands[2].type & ARM_GPR) {
|
|
k -= 6;
|
|
}
|
|
data = k;
|
|
data += encode1reg(op);
|
|
data += (op->operands[1].reg & 7) << (24 + 5);
|
|
data += (op->operands[1].reg >> 3) << 16;
|
|
if (op->operands[2].type & ARM_GPR) {
|
|
data += op->operands[2].reg << 8;
|
|
} else {
|
|
data += (op->operands[2].immediate & 0x3f) << 18;
|
|
data += (op->operands[2].immediate >> 6) << 8;
|
|
}
|
|
|
|
if (op->operands[2].type & ARM_CONSTANT && op->operands[3].type & ARM_SHIFT) {
|
|
if ((op->operands[3].shift == ARM_LSL) && (op->operands[3].shift_amount == 12)) {
|
|
data |= (0x4000);
|
|
}
|
|
}
|
|
|
|
if (op->operands[2].type & ARM_GPR && op->operands[3].type & ARM_SHIFT) {
|
|
switch (op->operands[3].shift) {
|
|
case ARM_LSL:
|
|
data |= (0x00040000 * op->operands[3].shift_amount);
|
|
break;
|
|
case ARM_LSR:
|
|
data |= (0x00040000 * op->operands[3].shift_amount) | (0x4000);
|
|
break;
|
|
case ARM_ASR:
|
|
data |= (0x00040000 * op->operands[3].shift_amount) | (0x8000);
|
|
break;
|
|
default:
|
|
return data;
|
|
}
|
|
}
|
|
return data;
|
|
}
|
|
|
|
static ut32 neg(ArmOp *op) {
|
|
if (op->operands_count < 2) {
|
|
return -1;
|
|
}
|
|
op->operands_count++;
|
|
op->operands[2] = op->operands[1];
|
|
op->operands[1].reg = 31; // xzr
|
|
|
|
return arithmetic(op, 0xd1); // sub reg0, xzr, reg1
|
|
}
|
|
|
|
static ut32 bitfield(ArmOp *op, int k) {
|
|
ut32 data = UT32_MAX;
|
|
check_cond(op->operands_count == 4);
|
|
check_cond(op->operands[0].type == ARM_GPR);
|
|
check_cond(op->operands[1].type == ARM_GPR);
|
|
check_cond(op->operands[0].reg_type == op->operands[1].reg_type);
|
|
check_cond(op->operands[2].type == ARM_CONSTANT);
|
|
check_cond(op->operands[3].type == ARM_CONSTANT);
|
|
int bits = (op->operands[0].reg_type & ARM_REG64) ? 64 : 32;
|
|
// unalias
|
|
if (!strcmp(op->mnemonic, "sbfx") || !strcmp(op->mnemonic, "ubfx")) {
|
|
op->operands[3].immediate += op->operands[2].immediate - 1;
|
|
} else if (!strcmp(op->mnemonic, "sbfiz") || !strcmp(op->mnemonic, "ubfiz")) {
|
|
check_cond(op->operands[2].immediate < bits);
|
|
int temp = bits - op->operands[2].immediate;
|
|
check_cond(op->operands[3].immediate <= temp);
|
|
op->operands[2].immediate = temp & (bits - 1);
|
|
op->operands[3].immediate--;
|
|
}
|
|
check_cond(op->operands[2].immediate < bits);
|
|
check_cond(op->operands[3].immediate < bits);
|
|
if (bits == 64) {
|
|
k |= 0x00004080;
|
|
}
|
|
k |= op->operands[2].immediate << 8 | op->operands[3].immediate << 18;
|
|
data = k | encode2regs(op);
|
|
return data;
|
|
}
|
|
|
|
static bool parseOperands(char *str, ArmOp *op) {
|
|
char *t = strdup(str);
|
|
int operand = 0;
|
|
char *token = t;
|
|
char *x;
|
|
int mem_opt = 0;
|
|
int msr_op_index = 0;
|
|
size_t index_bound = strcspn(t, "]");
|
|
if (!token) {
|
|
return false;
|
|
}
|
|
|
|
while (token) {
|
|
char *next = strchr(token, ',');
|
|
if (next) {
|
|
// Change the ',' in token to null byte
|
|
// essentialy split the token by commas
|
|
*next++ = '\0';
|
|
}
|
|
while (token[0] == ' ') {
|
|
token++;
|
|
}
|
|
if (operand >= MAX_OPERANDS) {
|
|
RZ_LOG_ERROR("assembler: arm64: maximum number of operands reached.\n");
|
|
return false;
|
|
}
|
|
op->operands[operand].type = ARM_NOTYPE;
|
|
op->operands[operand].reg_type = ARM_UNDEFINED;
|
|
|
|
// parse MSR (immediate) operand 1
|
|
if (strcmp(op->mnemonic, "msr") == 0 && operand == 1) {
|
|
|
|
// operand 1 must be a immediate
|
|
if (token[0] == '#' || (token[0] >= '0' && token[0] <= '9')) {
|
|
// immediate operand found.
|
|
op->operands[operand].sp_val = 0xfffe; // not regiter, but a immediate
|
|
op->operands[operand].immediate = rz_num_math(NULL, token[0] == '#' ? token + 1 : token);
|
|
operand++;
|
|
token = next;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// parse system registers
|
|
if ((strcmp(op->mnemonic, "mrs") == 0 && operand == 1) || (strcmp(op->mnemonic, "msr") == 0 && operand == 0)) {
|
|
for (msr_op_index = 0; msr_const[msr_op_index].name; msr_op_index++) {
|
|
if (strcasecmp(token, msr_const[msr_op_index].name) == 0) {
|
|
op->operands_count++;
|
|
op->operands[operand].type = ARM_CONSTANT;
|
|
op->operands[operand].immediate = msr_const[msr_op_index].val;
|
|
break;
|
|
}
|
|
}
|
|
if (msr_const[msr_op_index].name) {
|
|
operand++;
|
|
token = next;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
while (token[0] == ' ' || token[0] == '[' || token[0] == ']') {
|
|
token++;
|
|
}
|
|
|
|
if (!strncmp(token, "lsl", 3)) {
|
|
op->operands[operand].type = ARM_SHIFT;
|
|
op->operands[operand].shift = ARM_LSL;
|
|
} else if (!strncmp(token, "lsr", 3)) {
|
|
op->operands[operand].type = ARM_SHIFT;
|
|
op->operands[operand].shift = ARM_LSR;
|
|
} else if (!strncmp(token, "asr", 3)) {
|
|
op->operands[operand].type = ARM_SHIFT;
|
|
op->operands[operand].shift = ARM_ASR;
|
|
} else if (!strncmp(token, "ror", 3)) {
|
|
op->operands[operand].type = ARM_SHIFT;
|
|
op->operands[operand].shift = ARM_ROR;
|
|
} else if (!strncmp(token, "uxtb", 4)) {
|
|
op->operands[operand].type = ARM_EXTEND;
|
|
op->operands[operand].shift = ARM_UXTB;
|
|
} else if (!strncmp(token, "uxth", 4)) {
|
|
op->operands[operand].type = ARM_EXTEND;
|
|
op->operands[operand].shift = ARM_UXTH;
|
|
} else if (!strncmp(token, "uxtw", 4)) {
|
|
op->operands[operand].type = ARM_EXTEND;
|
|
op->operands[operand].shift = ARM_UXTW;
|
|
} else if (!strncmp(token, "uxtx", 4)) {
|
|
op->operands[operand].type = ARM_EXTEND;
|
|
op->operands[operand].shift = ARM_UXTX;
|
|
} else if (!strncmp(token, "sxtb", 4)) {
|
|
op->operands[operand].type = ARM_EXTEND;
|
|
op->operands[operand].shift = ARM_SXTB;
|
|
} else if (!strncmp(token, "sxth", 4)) {
|
|
op->operands[operand].type = ARM_EXTEND;
|
|
op->operands[operand].shift = ARM_SXTH;
|
|
} else if (!strncmp(token, "sxtw", 4)) {
|
|
op->operands[operand].type = ARM_EXTEND;
|
|
op->operands[operand].shift = ARM_SXTW;
|
|
} else if (!strncmp(token, "sxtx", 4)) {
|
|
op->operands[operand].type = ARM_EXTEND;
|
|
op->operands[operand].shift = ARM_SXTX;
|
|
}
|
|
if (op->operands[operand].type == ARM_SHIFT) {
|
|
op->operands_count++;
|
|
token += 3;
|
|
while (*token && *token == ' ') {
|
|
token++;
|
|
}
|
|
if (*token == '#') {
|
|
token++;
|
|
}
|
|
if (!*token || !isdigit(*token)) {
|
|
free(t);
|
|
return false;
|
|
}
|
|
op->operands[operand].shift_amount = rz_num_math(NULL, token);
|
|
op->operands[operand].amount_present = true;
|
|
if (op->operands[operand].shift_amount > 63) {
|
|
free(t);
|
|
return false;
|
|
}
|
|
operand++;
|
|
token = next;
|
|
continue;
|
|
}
|
|
if (op->operands[operand].type == ARM_EXTEND) {
|
|
op->operands_count++;
|
|
token += 4;
|
|
while (*token && *token == ' ') {
|
|
token++;
|
|
}
|
|
bool present = false;
|
|
if (*token == '#') {
|
|
present = true;
|
|
++token;
|
|
}
|
|
if (!*token || !isdigit(*token)) {
|
|
if (present) {
|
|
return false;
|
|
}
|
|
op->operands[operand].shift_amount = 0;
|
|
op->operands[operand].amount_present = false;
|
|
} else {
|
|
op->operands[operand].shift_amount = rz_num_math(NULL, token);
|
|
op->operands[operand].amount_present = true;
|
|
}
|
|
operand++;
|
|
token = next;
|
|
continue;
|
|
}
|
|
|
|
switch (token[0]) {
|
|
case 'x':
|
|
x = strchr(token, ',');
|
|
if (x) {
|
|
x[0] = '\0';
|
|
}
|
|
op->operands_count++;
|
|
op->operands[operand].type = ARM_GPR;
|
|
op->operands[operand].reg_type = ARM_REG64;
|
|
|
|
if (!strncmp(token + 1, "zr", 2)) {
|
|
// XZR
|
|
op->operands[operand].reg = 31;
|
|
} else {
|
|
op->operands[operand].reg = rz_num_math(NULL, token + 1);
|
|
}
|
|
|
|
if (op->operands[operand].reg > 31) {
|
|
free(t);
|
|
return false;
|
|
}
|
|
break;
|
|
case 'w':
|
|
op->operands_count++;
|
|
op->operands[operand].type = ARM_GPR;
|
|
op->operands[operand].reg_type = ARM_REG32;
|
|
|
|
if (!strncmp(token + 1, "zr", 2)) {
|
|
// WZR
|
|
op->operands[operand].reg = 31;
|
|
} else if (!strncmp(token + 1, "sp", 2)) {
|
|
// WSP
|
|
op->operands[operand].reg = 31;
|
|
op->operands[operand].reg_type |= ARM_SP;
|
|
} else {
|
|
op->operands[operand].reg = rz_num_math(NULL, token + 1);
|
|
}
|
|
|
|
if (op->operands[operand].reg > 31) {
|
|
free(t);
|
|
return false;
|
|
}
|
|
break;
|
|
case 'v':
|
|
op->operands_count++;
|
|
op->operands[operand].type = ARM_FP;
|
|
op->operands[operand].reg = rz_num_math(NULL, token + 1);
|
|
break;
|
|
case 's':
|
|
case 'S':
|
|
if (token[1] == 'P' || token[1] == 'p') {
|
|
int i;
|
|
for (i = 0; msr_const[i].name; i++) {
|
|
if (!rz_str_ncasecmp(token, msr_const[i].name, strlen(msr_const[i].name))) {
|
|
op->operands[operand].sp_val = msr_const[i].val;
|
|
break;
|
|
}
|
|
}
|
|
op->operands_count++;
|
|
op->operands[operand].type = ARM_GPR;
|
|
op->operands[operand].reg_type = ARM_SP | ARM_REG64;
|
|
op->operands[operand].reg = 31;
|
|
break;
|
|
}
|
|
mem_opt = get_mem_option(token);
|
|
if (mem_opt != -1) {
|
|
op->operands_count++;
|
|
op->operands[operand].type = ARM_MEM_OPT;
|
|
op->operands[operand].mem_option = mem_opt;
|
|
}
|
|
break;
|
|
case 'f':
|
|
if (token[1] == 'p') {
|
|
// lr register alias
|
|
op->operands_count++;
|
|
op->operands[operand].type = ARM_GPR;
|
|
op->operands[operand].reg_type = ARM_FP | ARM_REG64;
|
|
op->operands[operand].reg = 29;
|
|
break;
|
|
}
|
|
break;
|
|
case 'L':
|
|
case 'l':
|
|
case 'I':
|
|
case 'i':
|
|
case 'N':
|
|
case 'n':
|
|
case 'O':
|
|
case 'o':
|
|
case 'p':
|
|
case 'P':
|
|
if (token[1] == 'r') {
|
|
// lr register alias
|
|
op->operands_count++;
|
|
op->operands[operand].type = ARM_GPR;
|
|
op->operands[operand].reg_type = ARM_LR | ARM_REG64;
|
|
op->operands[operand].reg = 30;
|
|
break;
|
|
}
|
|
mem_opt = get_mem_option(token);
|
|
if (mem_opt != -1) {
|
|
op->operands_count++;
|
|
op->operands[operand].type = ARM_MEM_OPT;
|
|
op->operands[operand].mem_option = mem_opt;
|
|
}
|
|
break;
|
|
case '#':
|
|
if (token[1] == '-') {
|
|
op->operands[operand].sign = -1;
|
|
}
|
|
op->operands_count++;
|
|
op->operands[operand].type = ARM_CONSTANT;
|
|
op->operands[operand].immediate = rz_num_math(NULL, token + 1);
|
|
op->operands[operand].preindex = token - t < index_bound;
|
|
break;
|
|
case '-':
|
|
op->operands[operand].sign = -1;
|
|
// fallthrough
|
|
default:
|
|
op->operands_count++;
|
|
op->operands[operand].type = ARM_CONSTANT;
|
|
op->operands[operand].immediate = rz_num_math(NULL, token);
|
|
op->operands[operand].preindex = token - t < index_bound;
|
|
break;
|
|
}
|
|
token = next;
|
|
|
|
operand++;
|
|
if (operand > MAX_OPERANDS) {
|
|
free(t);
|
|
return false;
|
|
}
|
|
}
|
|
free(t);
|
|
return true;
|
|
}
|
|
|
|
static bool parseOpcode(const char *str, ArmOp *op) {
|
|
char *in = strdup(str);
|
|
char *space = strchr(in, ' ');
|
|
if (!space) {
|
|
op->operands[0].type = ARM_NOTYPE;
|
|
op->mnemonic = in;
|
|
return true;
|
|
}
|
|
space[0] = '\0';
|
|
op->mnemonic = in;
|
|
space++;
|
|
op->writeback = strstr(space, "]!") != NULL;
|
|
return parseOperands(space, op);
|
|
}
|
|
|
|
static bool handlePAC(ut32 *op, const char *str) {
|
|
if (!strcmp(str, "autiasp")) {
|
|
*op = 0xbf2303d5;
|
|
return true;
|
|
}
|
|
if (!strcmp(str, "autiaz")) {
|
|
*op = 0x9f2303d5;
|
|
return true;
|
|
}
|
|
if (!strcmp(str, "autibsp")) {
|
|
*op = 0xff2303d5;
|
|
return true;
|
|
}
|
|
if (!strcmp(str, "autibz")) {
|
|
*op = 0xdf2303d5;
|
|
return true;
|
|
}
|
|
if (!strcmp(str, "paciaz")) {
|
|
*op = 0x1f2303d5;
|
|
return true;
|
|
}
|
|
if (!strcmp(str, "pacibz")) {
|
|
*op = 0x5f2303d5;
|
|
return true;
|
|
}
|
|
if (!strcmp(str, "paciasp")) {
|
|
*op = 0x3f2303d5;
|
|
return true;
|
|
}
|
|
if (!strcmp(str, "pacibsp")) {
|
|
*op = 0x7f2303d5;
|
|
return true;
|
|
}
|
|
if (!strcmp(str, "retab")) {
|
|
*op = 0xff0f5fd6;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool arm64ass(const char *str, ut64 addr, ut32 *op) {
|
|
*op = UT32_MAX;
|
|
ArmOp ops = { 0 };
|
|
if (!parseOpcode(str, &ops)) {
|
|
free(ops.mnemonic);
|
|
return false;
|
|
}
|
|
/* TODO: write tests for this and move out the regsize logic into the mov */
|
|
if (!strncmp(str, "mov", 3)) {
|
|
*op = mov(&ops);
|
|
} else if (!strncmp(str, "cb", 2)) {
|
|
*op = cb(&ops);
|
|
} else if (!strncmp(str, "cmp", 3)) {
|
|
*op = cmp(&ops);
|
|
} else if (!strncmp(str, "ldrb", 4)) {
|
|
*op = lsop(&ops, 0x00004038, -1);
|
|
} else if (!strncmp(str, "ldrh", 4)) {
|
|
*op = lsop(&ops, 0x00004078, -1);
|
|
} else if (!strncmp(str, "ldrsh", 5)) {
|
|
*op = lsop(&ops, 0x00008078, -1);
|
|
} else if (!strncmp(str, "ldrsw", 5)) {
|
|
*op = lsop(&ops, 0x00000098, addr);
|
|
} else if (!strncmp(str, "ldrsb", 5)) {
|
|
*op = lsop(&ops, 0x00008038, -1);
|
|
} else if (!strncmp(str, "strb", 4)) {
|
|
*op = lsop(&ops, 0x00000038, -1);
|
|
} else if (!strncmp(str, "strh", 4)) {
|
|
*op = lsop(&ops, 0x00000078, -1);
|
|
} else if (!strncmp(str, "ldr", 3)) {
|
|
*op = reglsop(&ops, 0x000040f8);
|
|
} else if (!strncmp(str, "stur", 4)) {
|
|
*op = regsluop(&ops, 0x000000f8);
|
|
} else if (!strncmp(str, "ldur", 4)) {
|
|
*op = regsluop(&ops, 0x000040f8);
|
|
} else if (!strncmp(str, "str", 3)) {
|
|
*op = reglsop(&ops, 0x000000f8);
|
|
} else if (!strncmp(str, "stp", 3)) {
|
|
*op = stp(&ops, 0x000000a9);
|
|
} else if (!strncmp(str, "ldp", 3)) {
|
|
*op = stp(&ops, 0x000040a9);
|
|
} else if (!strncmp(str, "sub", 3)) { // w
|
|
*op = arithmetic(&ops, 0xd1);
|
|
} else if (!strncmp(str, "add x", 5)) {
|
|
*op = arithmetic(&ops, 0x91);
|
|
} else if (!strncmp(str, "add w", 5)) {
|
|
*op = arithmetic(&ops, 0x11);
|
|
} else if (!strncmp(str, "adr x", 5)) { // w
|
|
*op = adr(&ops, addr);
|
|
} else if (!strncmp(str, "adrp ", 5)) {
|
|
*op = adrp(&ops, addr);
|
|
} else if (!strncmp(str, "neg", 3)) {
|
|
*op = neg(&ops);
|
|
} else if (!strcmp(str, "isb")) {
|
|
*op = 0xdf3f03d5;
|
|
} else if (handlePAC(op, str)) { // PAC
|
|
free(ops.mnemonic);
|
|
return true;
|
|
} else if (!strcmp(str, "nop")) {
|
|
*op = 0x1f2003d5;
|
|
} else if (!strcmp(str, "ret")) {
|
|
*op = 0xc0035fd6;
|
|
} else if (!strncmp(str, "msr ", 4)) {
|
|
*op = msr(&ops, 0);
|
|
} else if (!strncmp(str, "mrs ", 4)) {
|
|
*op = msr(&ops, 1);
|
|
} else if (!strncmp(str, "ands ", 5)) {
|
|
*op = logical(&ops, false, ARM_ANDS);
|
|
} else if (!strncmp(str, "and ", 4)) {
|
|
*op = logical(&ops, false, ARM_AND);
|
|
} else if (!strncmp(str, "bics ", 5)) {
|
|
*op = logical(&ops, true, ARM_ANDS);
|
|
} else if (!strncmp(str, "bic ", 4)) {
|
|
*op = logical(&ops, true, ARM_AND);
|
|
} else if (!strncmp(str, "eon ", 4)) {
|
|
*op = logical(&ops, true, ARM_EOR);
|
|
} else if (!strncmp(str, "eor ", 4)) {
|
|
*op = logical(&ops, false, ARM_EOR);
|
|
} else if (!strncmp(str, "orn ", 4)) {
|
|
*op = logical(&ops, true, ARM_ORR);
|
|
} else if (!strncmp(str, "orr ", 4)) {
|
|
*op = logical(&ops, false, ARM_ORR);
|
|
} else if (!strncmp(str, "svc ", 4)) { // system level exception
|
|
*op = exception(&ops, 0x010000d4);
|
|
} else if (!strncmp(str, "hvc ", 4)) { // hypervisor level exception
|
|
*op = exception(&ops, 0x020000d4);
|
|
} else if (!strncmp(str, "smc ", 4)) { // secure monitor exception
|
|
*op = exception(&ops, 0x030000d4);
|
|
} else if (!strncmp(str, "brk ", 4)) { // breakpoint
|
|
*op = exception(&ops, 0x000020d4);
|
|
} else if (!strncmp(str, "hlt ", 4)) { // halt
|
|
*op = exception(&ops, 0x000040d4);
|
|
} else if (!strncmp(str, "b ", 2)) {
|
|
*op = branch(&ops, addr, 0x14);
|
|
} else if (!strncmp(str, "b.", 2)) {
|
|
*op = parse_bdot(str + 2, &ops, addr);
|
|
} else if (!strncmp(str, "bl ", 3)) {
|
|
*op = branch(&ops, addr, 0x94);
|
|
} else if (!strncmp(str, "br x", 4)) {
|
|
*op = branch(&ops, addr, 0x1fd6);
|
|
} else if (!strncmp(str, "blr x", 5)) {
|
|
*op = branch(&ops, addr, 0x3fd6);
|
|
} else if (!strncmp(str, "dmb ", 4)) {
|
|
*op = mem_barrier(&ops, addr, 0xbf3003d5);
|
|
} else if (!strncmp(str, "dsb ", 4)) {
|
|
*op = mem_barrier(&ops, addr, 0x9f3003d5);
|
|
} else if (!strncmp(str, "isb", 3)) {
|
|
*op = mem_barrier(&ops, addr, 0xdf3f03d5);
|
|
} else if (!strncmp(str, "sbfiz ", 6) || !strncmp(str, "sbfm ", 5) || !strncmp(str, "sbfx ", 5)) {
|
|
*op = bitfield(&ops, 0x00000013);
|
|
} else if (!strncmp(str, "ubfiz ", 6) || !strncmp(str, "ubfm ", 5) || !strncmp(str, "ubfx ", 5)) {
|
|
*op = bitfield(&ops, 0x00000053);
|
|
}
|
|
free(ops.mnemonic);
|
|
return *op != UT32_MAX;
|
|
}
|