New arm64 instruction support (#8140)

* Add cmp for reg, reg

* Add strb instruction to arm64

* Add b.eq instruction arm64

* Add byte store ops

* Add str and ldr ops
This commit is contained in:
Sven Steinbauer 2017-08-07 12:42:46 +01:00 committed by radare
parent f20edcc5ee
commit 6c666bf142

View File

@ -23,6 +23,13 @@ typedef enum regtype_t {
ARM_SIMD = 16
} RegType;
typedef enum shifttype_t {
ARM_NO_SHIFT = -1,
ARM_LSL = 0,
ARM_LSR = 1,
ARM_ASR = 2
} ShiftType;
typedef struct operand_t {
OpType type;
union {
@ -36,8 +43,8 @@ typedef struct operand_t {
int sign;
};
struct {
ut64 lsl;
ut64 shift;
ut64 shift_amount;
ShiftType shift;
};
struct {
ut32 mem_option;
@ -60,7 +67,7 @@ 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};
"xxx", "osh", "oshst", "oshld", NULL};
int i = 0;
while (options[i]) {
if (!strcasecmp (token, options[i])) {
@ -71,6 +78,121 @@ static int get_mem_option(char *token) {
return -1;
}
static int countLeadingZeros(ut32 x) {
int count = 0;
while (x) {
x >>= 1;
--count;
}
return count;
}
static int countTrailingZeros(ut32 x) {
int count = 0;
while (x > 0) {
if ((x & 1) == 1) {
break;
} else {
count ++;
x = 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 rol(ut32 n, int i) {
return ((n << i) | (n >> (32 - i))) >> 0;
}
static int ror(ut32 n, int i) {
return ((n >> i) | (n << (32 - i))) >> 0;
}
static int encode(int n) {
int i, m;
for (i = 0; i < 16; i++) {
m = rol(n, i * 2);
if (m < 256) {
return (i << 8) | m;
}
}
return -1;
}
static int countLeadingOnes(ut32 x) {
return countLeadingZeros (~x);
}
static int countTrailingOnes(ut32 x) {
return countTrailingZeros (~x);
}
static bool isMask(ut32 value) {
return value && ((value + 1) & value) == 0;
}
static bool isShiftedMask (ut32 value) {
return value && isMask ((value - 1) | value);
}
static ut32 decodeBitMasks(ut32 imm) {
// get element size
int size = 32;
// determine rot to make element be 0^m 1^n
ut32 cto, i;
ut32 mask = ((ut64) - 1LL) >> (64 - size);
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 - 1) << 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;
ut64 encoding = (n << 12) | (immr << 6) | (nimms & 0x3f);
return encoding;
}
static ut32 mov(ArmOp *op) {
int k = 0;
ut32 data = UT32_MAX;
@ -112,13 +234,202 @@ static ut32 mov(ArmOp *op) {
}
data = k;
//printf ("Immediate %d\n", op->operands[1].immediate);
data |= (op->operands[0].reg << 24); // arg(0)
data |= ((op->operands[1].immediate & 7) << 29); // arg(1)
data |= (((op->operands[1].immediate >> 3) & 0xff) << 16); // arg(1)
data |= ((op->operands[1].immediate >> 10) << 7); // arg(1)
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].shift != ARM_NO_SHIFT) {
data |= op->operands[2].shift_amount << 18 | op->operands[2].shift << 14;
}
return data;
}
static ut32 sturop(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 | op->operands[0].reg << 24 | op->operands[1].reg << 29 | (op->operands[1].reg & 56) << 13;
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 | op->operands[0].reg << 24 | op->operands[1].reg << 29 | (op->operands[1].reg & 56) << 13;
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 | op->operands[0].reg << 24 | op->operands[1].reg << 29 | (op->operands[1].reg & 56) << 13;
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;
}
// Byte load/store ops
static ut32 bytelsop(ArmOp *op, int k) {
ut32 data = UT32_MAX;
if (op->operands[0].reg_type & ARM_REG64) {
return data;
}
if (op->operands[1].reg_type & ARM_REG32) {
return data;
}
if (op->operands[2].type & ARM_GPR) {
if ((k & 0xf) != 8) {
k--;
}
k += 0x00682000;
data = k | op->operands[0].reg << 24 | op->operands[1].reg << 29 | (op->operands[1].reg & 56) << 13;
data |= op->operands[2].reg << 8;
return data;
}
int n = op->operands[2].immediate;
if (n > 0xfff || n < -0x100) {
return UT32_MAX;
}
// Half ops
int halfop = false;
if ((k & 0xf) == 8) {
halfop = true;
if (n == 0 || (countTrailingZeros(n) && n > 0)) {
k++;
}
} else {
if (n < 0) {
k--;
}
}
data = k | op->operands[0].reg << 24 | op->operands[1].reg << 29 | (op->operands[1].reg & 56) << 13;
int imm = n;
int low_shift = 20;
int high_shift = 8;
int top_shift = 10;
if (n < 0) {
imm = 0xfff + (n + 1);
}
if (halfop) {
if (imm & 0x1 || n < 0) {
data |= (0xf & imm) << low_shift ;
data |= (0x7 & (imm >> 4)) << high_shift;
data |= (0x7 & (imm >> 6)) << top_shift;
} else {
data |= (0xf & imm) << (low_shift - 3);
data |= (0x7 & (imm >> 4)) << (high_shift + 13);
data |= (0x7 & (imm >> 7)) << (top_shift - 2);
}
} else {
if (n < 0) {
data |= (0xf & imm) << 20;
data |= (0x1f & (imm >> 4)) << 8;
} else {
data |= (0xf & imm) << 18;
data |= (0x3 & (imm >> 4)) << 22;
data |= (0x7 & (imm >> 6)) << 8;
}
}
return data;
}
static ut32 branch(ArmOp *op, ut64 addr, int k) {
@ -154,6 +465,37 @@ static ut32 branch(ArmOp *op, ut64 addr, int k) {
return data;
}
static ut32 bdot(ArmOp *op, ut64 addr, int k) {
ut32 data = UT32_MAX;
int n = 0;
int a = 0;
int tz = 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;
}
static ut32 mem_barrier (ArmOp *op, ut64 addr, int k) {
ut32 data = UT32_MAX;
data = k;
@ -231,81 +573,6 @@ static ut32 msr(ArmOp *op, int w) {
return data;
}
static int countLeadingZeros(ut32 x) {
int count = 0;
while (x) {
x >>= 1;
--count;
}
return count;
}
static int countTrailingZeros(ut32 x) {
int count = 0;
while (x > 0) {
if ((x & 1) == 1) {
break;
} else {
count ++;
x = x >> 1;
}
}
return count;
}
static int countLeadingOnes(ut32 x) {
return countLeadingZeros (~x);
}
static int countTrailingOnes(ut32 x) {
return countTrailingZeros (~x);
}
static bool isMask(ut32 value) {
return value && ((value + 1) & value) == 0;
}
static bool isShiftedMask (ut32 value) {
return value && isMask ((value - 1) | value);
}
static ut32 decodeBitMasks(ut32 imm) {
// get element size
int size = 32;
// determine rot to make element be 0^m 1^n
ut32 cto, i;
ut32 mask = ((ut64) - 1LL) >> (64 - size);
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 - 1) << 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;
ut64 encoding = (n << 12) | (immr << 6) | (nimms & 0x3f);
return encoding;
}
static ut32 orr(ArmOp *op, int addr) {
ut32 data = UT32_MAX;
@ -390,7 +657,6 @@ static ut32 adrp(ArmOp *op, ut64 addr, ut32 k) { //, int reg, ut64 dst) {
return data;
}
static ut32 adr(ArmOp *op, int addr) {
ut32 data = UT32_MAX;
ut64 at = 0LL;
@ -471,6 +737,30 @@ static bool parseOperands(char* str, ArmOp *op) {
}
op->operands[operand].type = ARM_NOTYPE;
op->operands[operand].reg_type = ARM_UNDEFINED;
op->operands[operand].shift = ARM_NO_SHIFT;
while (token[0] == ' ' || token[0] == '[' || token[0] == ']') {
token ++;
}
if (!strncmp (token, "lsl", 3)) {
op->operands[operand].shift = ARM_LSL;
} else if (!strncmp (token, "lsr", 3)) {
op->operands[operand].shift = ARM_LSR;
} else if (!strncmp (token, "asr", 3)) {
op->operands[operand].shift = ARM_ASR;
}
if (op->operands[operand].shift != ARM_NO_SHIFT) {
op->operands_count ++;
op->operands[operand].shift_amount = r_num_math (NULL, token + 4);
if (op->operands[operand].shift_amount > 63) {
return false;
}
operand ++;
token = next;
continue;
}
switch (token[0]) {
case 'x':
x = strchr (token, ',');
@ -481,12 +771,18 @@ static bool parseOperands(char* str, ArmOp *op) {
op->operands[operand].type = ARM_GPR;
op->operands[operand].reg_type = ARM_REG64;
op->operands[operand].reg = r_num_math (NULL, token + 1);
if (op->operands[operand].reg > 31) {
return false;
}
break;
case 'w':
op->operands_count ++;
op->operands[operand].type = ARM_GPR;
op->operands[operand].reg_type = ARM_REG32;
op->operands[operand].reg = r_num_math (NULL, token + 1);
if (op->operands[operand].reg > 31) {
return false;
}
break;
case 'v':
op->operands_count ++;
@ -506,7 +802,7 @@ static bool parseOperands(char* str, ArmOp *op) {
op->operands_count ++;
op->operands[operand].type = ARM_GPR;
op->operands[operand].reg_type = ARM_SP | ARM_REG64;
op->operands[operand].reg = r_num_math (NULL, token + 1);
op->operands[operand].reg = 31;
break;
}
mem_opt = get_mem_option (token);
@ -544,6 +840,7 @@ static bool parseOperands(char* str, ArmOp *op) {
break;
}
token = next;
operand ++;
if (operand > MAX_OPERANDS) {
free (t);
@ -571,13 +868,57 @@ static bool parseOpcode(const char *str, ArmOp *op) {
bool arm64ass(const char *str, ut64 addr, ut32 *op) {
ArmOp ops = {0};
if (!parseOpcode (str, &ops)) {
return -1;
return false;
}
/* TODO: write tests for this and move out the regsize logic into the mov */
if (!strncmp (str, "mov", 3)) {
*op = mov (&ops);
return *op != -1;
}
if (!strncmp (str, "cmp", 3)) {
*op = cmp (&ops);
return *op != -1;
}
if (!strncmp (str, "ldrb", 4)) {
*op = bytelsop (&ops, 0x00004039);
return *op != -1;
}
if (!strncmp (str, "ldrh", 4)) {
*op = bytelsop (&ops, 0x00004078);
return *op != -1;
}
if (!strncmp (str, "ldrsh", 5)) {
*op = bytelsop (&ops, 0x0000c078);
return *op != -1;
}
if (!strncmp (str, "ldrsw", 5)) {
*op = bytelsop (&ops, 0x000080b8);
return *op != -1;
}
if (!strncmp (str, "ldrsb", 5)) {
*op = bytelsop (&ops, 0x0000c039);
return *op != -1;
}
if (!strncmp (str, "strb", 4)) {
*op = bytelsop (&ops, 0x00000039);
return *op != -1;
}
if (!strncmp (str, "strh", 4)) {
*op = bytelsop (&ops, 0x00000078);
return *op != -1;
}
if (!strncmp (str, "ldr", 3)) {
*op = reglsop (&ops, 0x000040f8);
return *op != -1;
}
if (!strncmp (str, "stur", 4)) {
*op = sturop (&ops, 0x000000f8);
return *op != -1;
}
if (!strncmp (str, "str", 3)) {
*op = reglsop (&ops, 0x000000f8);
return *op != -1;
}
if (!strncmp (str, "sub", 3)) { // w
*op = arithmetic (&ops, 0xd1);
return *op != -1;
@ -591,7 +932,7 @@ bool arm64ass(const char *str, ut64 addr, ut32 *op) {
return *op != -1;
}
if (!strncmp (str, "adrp x", 6)) {
*op = adrp (&ops, addr, 0x000000d0);
*op = adrp (&ops, addr, 0x00000090);
return *op != -1;
}
if (!strcmp (str, "nop")) {
@ -642,6 +983,14 @@ bool arm64ass(const char *str, ut64 addr, ut32 *op) {
*op = branch (&ops, addr, 0x14);
return *op != -1;
}
if (!strncmp (str, "b.eq ", 5)) {
*op = bdot (&ops, addr, 0x00000054);
return *op != -1;
}
if (!strncmp (str, "b.hs ", 5)) {
*op = bdot (&ops, addr, 0x02000054);
return *op != -1;
}
if (!strncmp (str, "bl ", 3)) {
*op = branch (&ops, addr, 0x94);
return *op != -1;