ARM64 disassembler: support for LDR instructions
Implemented disassembly for a whole bunch of various ldr instructions. Obtained from: Semihalf Sponsored by: Cavium Approved by: cognet (mentor) Reviewed by: zbb Differential revision: https://reviews.freebsd.org/D5217
This commit is contained in:
parent
826ad6942a
commit
a33f5ef2d9
@ -38,6 +38,16 @@ __FBSDID("$FreeBSD$");
|
||||
#define ARM64_MAX_TOKEN_LEN 8
|
||||
#define ARM64_MAX_TOKEN_CNT 10
|
||||
|
||||
#define ARM_INSN_SIZE_OFFSET 30
|
||||
#define ARM_INSN_SIZE_MASK 0x3
|
||||
|
||||
/* Special options for instruction printing */
|
||||
#define OP_SIGN_EXT (1UL << 0) /* Sign-extend immediate value */
|
||||
#define OP_LITERAL (1UL << 1) /* Use literal (memory offset) */
|
||||
#define OP_MULT_4 (1UL << 2) /* Multiply immediate by 4 */
|
||||
#define OP_SF32 (1UL << 3) /* Force 32-bit access */
|
||||
#define OP_SF_INV (1UL << 6) /* SF is inverted (1 means 32 bit access) */
|
||||
|
||||
static const char *w_reg[] = {
|
||||
"w0", "w1", "w2", "w3", "w4", "w5", "w6", "w7",
|
||||
"w8", "w9", "w10", "w11", "w12", "w13", "w14", "w15",
|
||||
@ -74,6 +84,10 @@ struct arm64_insn_token {
|
||||
enum arm64_format_type {
|
||||
TYPE_01, /* OP <RD>, <RN>, <RM>{, <shift [LSL, LSR, ASR]> #<imm>} SF32/64
|
||||
OP <RD>, <RN>, #<imm>{, <shift [0, 12]>} SF32/64 */
|
||||
TYPE_02, /* OP <RT>, [<RN>, #<imm>]{!}] SF32/64
|
||||
OP <RT>, [<RN>], #<imm>{!} SF32/64
|
||||
OP <RT>, <RN>, <RM> {, EXTEND AMOUNT } */
|
||||
TYPE_03, /* OP <RT>, #imm SF32/64 */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -112,12 +126,57 @@ struct arm64_insn {
|
||||
* SHIFT - type of shift (instruction dependent)
|
||||
* IMM - immediate value
|
||||
* Rx - register number
|
||||
* OPTION - command specific options
|
||||
* SCALE - scaling of immediate value
|
||||
*/
|
||||
static struct arm64_insn arm64_i[] = {
|
||||
{ "add", "SF(1)|0001011|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)", TYPE_01, 0 },
|
||||
{ "mov", "SF(1)|001000100000000000000|RN(5)|RD(5)", TYPE_01, 0 },
|
||||
{ "add", "SF(1)|0010001|SHIFT(2)|IMM(12)|RN(5)|RD(5)", TYPE_01, 0 },
|
||||
{ NULL, NULL }
|
||||
{ "add", "SF(1)|0001011|SHIFT(2)|0|RM(5)|IMM(6)|RN(5)|RD(5)",
|
||||
TYPE_01, 0 },
|
||||
{ "mov", "SF(1)|001000100000000000000|RN(5)|RD(5)",
|
||||
TYPE_01, 0 },
|
||||
{ "add", "SF(1)|0010001|SHIFT(2)|IMM(12)|RN(5)|RD(5)",
|
||||
TYPE_01, 0 },
|
||||
{ "ldr", "1|SF(1)|111000010|IMM(9)|OPTION(2)|RN(5)|RT(5)",
|
||||
TYPE_02, OP_SIGN_EXT }, /* ldr immediate post/pre index */
|
||||
{ "ldr", "1|SF(1)|11100101|IMM(12)|RN(5)|RT(5)",
|
||||
TYPE_02, 0 }, /* ldr immediate unsigned */
|
||||
{ "ldr", "1|SF(1)|111000011|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
|
||||
TYPE_02, 0 }, /* ldr register */
|
||||
{ "ldr", "0|SF(1)|011000|IMM(19)|RT(5)",
|
||||
TYPE_03, OP_SIGN_EXT | OP_LITERAL | OP_MULT_4 }, /* ldr literal */
|
||||
{ "ldrb", "00|111000010|IMM(9)|OPTION(2)|RN(5)|RT(5)",
|
||||
TYPE_02, OP_SIGN_EXT | OP_SF32 }, /* ldrb immediate post/pre index */
|
||||
{ "ldrb", "00|11100101|IMM(12)|RN(5)|RT(5)",
|
||||
TYPE_02, OP_SF32 }, /* ldrb immediate unsigned */
|
||||
{ "ldrb", "00|111000011|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
|
||||
TYPE_02, OP_SF32 }, /* ldrb register */
|
||||
{ "ldrh", "01|111000010|IMM(9)|OPTION(2)|RN(5)|RT(5)", TYPE_02,
|
||||
OP_SIGN_EXT | OP_SF32 }, /* ldrh immediate post/pre index */
|
||||
{ "ldrh", "01|11100101|IMM(12)|RN(5)|RT(5)",
|
||||
TYPE_02, OP_SF32 }, /* ldrh immediate unsigned */
|
||||
{ "ldrh", "01|111000011|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
|
||||
TYPE_02, OP_SF32 }, /* ldrh register */
|
||||
{ "ldrsb", "001110001|SF(1)|0|IMM(9)|OPTION(2)|RN(5)|RT(5)",
|
||||
TYPE_02, OP_SIGN_EXT | OP_SF_INV }, /* ldrsb immediate post/pre index */
|
||||
{ "ldrsb", "001110011|SF(1)|IMM(12)|RN(5)|RT(5)",\
|
||||
TYPE_02, OP_SF_INV}, /* ldrsb immediate unsigned */
|
||||
{ "ldrsb", "001110001|SF(1)|1|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
|
||||
TYPE_02, OP_SF_INV }, /* ldrsb register */
|
||||
{ "ldrsh", "011110001|SF(1)|0|IMM(9)|OPTION(2)|RN(5)|RT(5)",
|
||||
TYPE_02, OP_SIGN_EXT | OP_SF_INV }, /* ldrsh immediate post/pre index */
|
||||
{ "ldrsh", "011110011|SF(1)|IMM(12)|RN(5)|RT(5)",
|
||||
TYPE_02, OP_SF_INV}, /* ldrsh immediate unsigned */
|
||||
{ "ldrsh", "011110001|SF(1)|1|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
|
||||
TYPE_02, OP_SF_INV }, /* ldrsh register */
|
||||
{ "ldrsw", "10111000100|IMM(9)|OPTION(2)|RN(5)|RT(5)",
|
||||
TYPE_02, OP_SIGN_EXT }, /* ldrsw immediate post/pre index */
|
||||
{ "ldrsw", "1011100110|IMM(12)|RN(5)|RT(5)",
|
||||
TYPE_02, 0 }, /* ldrsw immediate unsigned */
|
||||
{ "ldrsw", "10111000101|RM(5)|OPTION(3)|SCALE(1)|10|RN(5)|RT(5)",
|
||||
TYPE_02, 0 }, /* ldrsw register */
|
||||
{ "ldrsw", "10011000|IMM(19)|RT(5)",
|
||||
TYPE_03, OP_SIGN_EXT | OP_LITERAL | OP_MULT_4 }, /* ldr literal */
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static void
|
||||
@ -240,6 +299,29 @@ arm64_disasm_read_token(struct arm64_insn *insn, u_int opcode,
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
static int
|
||||
arm64_disasm_read_token_sign_ext(struct arm64_insn *insn, u_int opcode,
|
||||
const char *token, int *val)
|
||||
{
|
||||
int i;
|
||||
int msk;
|
||||
|
||||
for (i = 0; i < ARM64_MAX_TOKEN_CNT; i++) {
|
||||
if (strcmp(insn->tokens[i].name, token) == 0) {
|
||||
msk = (1 << insn->tokens[i].len) - 1;
|
||||
*val = ((opcode >> insn->tokens[i].pos) & msk);
|
||||
|
||||
/* If last bit is 1, sign-extend the value */
|
||||
if (*val & (1 << (insn->tokens[i].len - 1)))
|
||||
*val |= ~msk;
|
||||
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
static const char *
|
||||
arm64_reg(int b64, int num)
|
||||
{
|
||||
@ -257,11 +339,17 @@ disasm(const struct disasm_interface *di, vm_offset_t loc, int altfmt)
|
||||
uint32_t insn;
|
||||
int matchp;
|
||||
int ret;
|
||||
int shift, rm, rd, rn, imm, sf;
|
||||
int shift, rm, rt, rd, rn, imm, sf, idx, option, scale, amount;
|
||||
int sign_ext;
|
||||
int rm_absent;
|
||||
/* Indicate if immediate should be outside or inside brackets */
|
||||
int inside;
|
||||
/* Print exclamation mark if pre-incremented */
|
||||
int pre;
|
||||
|
||||
/* Initialize defaults, all are 0 except SF indicating 64bit access */
|
||||
shift = rd = rm = rn = imm = 0;
|
||||
shift = rd = rm = rn = imm = idx = option = amount = scale = 0;
|
||||
sign_ext = 0;
|
||||
sf = 1;
|
||||
|
||||
matchp = 0;
|
||||
@ -278,14 +366,33 @@ disasm(const struct disasm_interface *di, vm_offset_t loc, int altfmt)
|
||||
if (matchp == 0)
|
||||
goto undefined;
|
||||
|
||||
/* Global options */
|
||||
if (i_ptr->special_ops & OP_SF32)
|
||||
sf = 0;
|
||||
|
||||
/* Global optional tokens */
|
||||
arm64_disasm_read_token(i_ptr, insn, "SF", &sf);
|
||||
if (i_ptr->special_ops & OP_SF_INV)
|
||||
sf = 1 - sf;
|
||||
if (arm64_disasm_read_token(i_ptr, insn, "SIGN", &sign_ext) == 0)
|
||||
sign_ext = 1 - sign_ext;
|
||||
if (i_ptr->special_ops & OP_SIGN_EXT)
|
||||
sign_ext = 1;
|
||||
if (sign_ext != 0)
|
||||
arm64_disasm_read_token_sign_ext(i_ptr, insn, "IMM", &imm);
|
||||
else
|
||||
arm64_disasm_read_token(i_ptr, insn, "IMM", &imm);
|
||||
if (i_ptr->special_ops & OP_MULT_4)
|
||||
imm <<= 2;
|
||||
|
||||
/* Print opcode by type */
|
||||
switch (i_ptr->type) {
|
||||
case TYPE_01:
|
||||
/* OP <RD>, <RN>, <RM>{, <shift [LSL, LSR, ASR]> #<imm>} SF32/64
|
||||
OP <RD>, <RN>, #<imm>{, <shift [0, 12]>} SF32/64 */
|
||||
|
||||
/* Mandatory tokens */
|
||||
ret = arm64_disasm_read_token(i_ptr, insn, "SF", &sf);
|
||||
ret |= arm64_disasm_read_token(i_ptr, insn, "RD", &rd);
|
||||
ret = arm64_disasm_read_token(i_ptr, insn, "RD", &rd);
|
||||
ret |= arm64_disasm_read_token(i_ptr, insn, "RN", &rn);
|
||||
if (ret != 0) {
|
||||
printf("ERROR: Missing mandatory token for op %s type %d\n",
|
||||
@ -294,7 +401,6 @@ disasm(const struct disasm_interface *di, vm_offset_t loc, int altfmt)
|
||||
}
|
||||
|
||||
/* Optional tokens */
|
||||
arm64_disasm_read_token(i_ptr, insn, "IMM", &imm);
|
||||
arm64_disasm_read_token(i_ptr, insn, "SHIFT", &shift);
|
||||
rm_absent = arm64_disasm_read_token(i_ptr, insn, "RM", &rm);
|
||||
|
||||
@ -312,6 +418,115 @@ disasm(const struct disasm_interface *di, vm_offset_t loc, int altfmt)
|
||||
if (shift != 0)
|
||||
di->di_printf(" LSL #12");
|
||||
}
|
||||
break;
|
||||
case TYPE_02:
|
||||
/* OP <RT>, [<RN>, #<imm>]{!}] SF32/64
|
||||
OP <RT>, [<RN>], #<imm>{!} SF32/64
|
||||
OP <RT>, <RN>, <RM> {, EXTEND AMOUNT } */
|
||||
|
||||
/* Mandatory tokens */
|
||||
ret = arm64_disasm_read_token(i_ptr, insn, "RT", &rt);
|
||||
ret |= arm64_disasm_read_token(i_ptr, insn, "RN", &rn);
|
||||
if (ret != 0) {
|
||||
printf("ERROR: Missing mandatory token for op %s type %d\n",
|
||||
i_ptr->name, i_ptr->type);
|
||||
goto undefined;
|
||||
}
|
||||
|
||||
/* Optional tokens */
|
||||
arm64_disasm_read_token(i_ptr, insn, "OPTION", &option);
|
||||
arm64_disasm_read_token(i_ptr, insn, "SCALE", &scale);
|
||||
rm_absent = arm64_disasm_read_token(i_ptr, insn, "RM", &rm);
|
||||
|
||||
if (rm_absent) {
|
||||
/*
|
||||
* In unsigned operation, shift immediate value
|
||||
* and reset options to default.
|
||||
*/
|
||||
if (sign_ext == 0) {
|
||||
imm = imm << ((insn >> ARM_INSN_SIZE_OFFSET) &
|
||||
ARM_INSN_SIZE_MASK);
|
||||
option = 0;
|
||||
}
|
||||
switch (option) {
|
||||
case 0x0:
|
||||
pre = 0;
|
||||
inside = 1;
|
||||
break;
|
||||
case 0x1:
|
||||
pre = 0;
|
||||
inside = 0;
|
||||
break;
|
||||
case 0x2:
|
||||
default:
|
||||
pre = 1;
|
||||
inside = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
di->di_printf("%s\t%s, ", i_ptr->name, arm64_reg(sf, rt));
|
||||
if (inside != 0) {
|
||||
di->di_printf("[%s", arm64_reg(1, rn));
|
||||
if (imm != 0)
|
||||
di->di_printf(", #%d", imm);
|
||||
di->di_printf("]");
|
||||
} else {
|
||||
di->di_printf("[%s]", arm64_reg(1, rn));
|
||||
if (imm != 0)
|
||||
di->di_printf(", #%d", imm);
|
||||
}
|
||||
if (pre != 0)
|
||||
di->di_printf("!");
|
||||
} else {
|
||||
/* Last bit of option field determines 32/64 bit offset */
|
||||
di->di_printf("%s\t%s, [%s, %s", i_ptr->name,
|
||||
arm64_reg(sf, rt), arm64_reg(1, rn),
|
||||
arm64_reg(option & 1, rm));
|
||||
|
||||
/* Calculate amount, it's op(31:30) */
|
||||
amount = (insn >> ARM_INSN_SIZE_OFFSET) &
|
||||
ARM_INSN_SIZE_MASK;
|
||||
|
||||
switch (option) {
|
||||
case 0x2:
|
||||
di->di_printf(", uxtw #%d", amount);
|
||||
break;
|
||||
case 0x3:
|
||||
if (scale != 0)
|
||||
di->di_printf(", lsl #%d", amount);
|
||||
break;
|
||||
case 0x6:
|
||||
di->di_printf(", sxtw #%d", amount);
|
||||
break;
|
||||
case 0x7:
|
||||
di->di_printf(", sxts #%d", amount);
|
||||
break;
|
||||
default:
|
||||
di->di_printf(", RSVD");
|
||||
break;
|
||||
}
|
||||
di->di_printf("]");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case TYPE_03:
|
||||
/* OP <RT>, #imm SF32/64 */
|
||||
|
||||
/* Mandatory tokens */
|
||||
ret = arm64_disasm_read_token(i_ptr, insn, "RT", &rt);
|
||||
if (ret != 0) {
|
||||
printf("ERROR: Missing mandatory token for op %s type %d\n",
|
||||
i_ptr->name, i_ptr->type);
|
||||
goto undefined;
|
||||
}
|
||||
|
||||
di->di_printf("%s\t%s, ", i_ptr->name, arm64_reg(sf, rt));
|
||||
if (i_ptr->special_ops & OP_LITERAL)
|
||||
di->di_printf("0x%lx", loc + imm);
|
||||
else
|
||||
di->di_printf("#%d", imm);
|
||||
|
||||
break;
|
||||
default:
|
||||
goto undefined;
|
||||
|
Loading…
x
Reference in New Issue
Block a user