diff options
| author | Rusty Wagner <rusty.wagner@gmail.com> | 2024-03-05 19:50:13 -0500 |
|---|---|---|
| committer | Rusty Wagner <rusty.wagner@gmail.com> | 2024-03-05 20:34:34 -0500 |
| commit | e093c21ed880ac3eb72119be15093ee04f8ce299 (patch) | |
| tree | 9f720ebdc0ae415734b1199ed341668c69710a94 /arch/arm64/disassembler/format.c | |
| parent | 0609276712622908254065546102381466033141 (diff) | |
Move architecture modules into the API repo
Diffstat (limited to 'arch/arm64/disassembler/format.c')
| -rw-r--r-- | arch/arm64/disassembler/format.c | 607 |
1 files changed, 607 insertions, 0 deletions
diff --git a/arch/arm64/disassembler/format.c b/arch/arm64/disassembler/format.c new file mode 100644 index 00000000..4867a7aa --- /dev/null +++ b/arch/arm64/disassembler/format.c @@ -0,0 +1,607 @@ +#include <stdbool.h> +#include <string.h> +#include <stdio.h> +#include <stdint.h> +#include <inttypes.h> + +#include "format.h" +#include "regs.h" +#include "pcode.h" + +const char *get_arrspec_str(ArrangementSpec arrspec) +{ + switch(arrspec) { + case ARRSPEC_FULL: return ".1q"; + case ARRSPEC_2DOUBLES: return ".2d"; + case ARRSPEC_4SINGLES: return ".4s"; + case ARRSPEC_8HALVES: return ".8h"; + case ARRSPEC_16BYTES: return ".16b"; + case ARRSPEC_1DOUBLE: return ".1d"; + case ARRSPEC_2SINGLES: return ".2s"; + case ARRSPEC_4HALVES: return ".4h"; + case ARRSPEC_8BYTES: return ".8b"; + case ARRSPEC_1SINGLE: return ".1s"; + case ARRSPEC_2HALVES: return ".2h"; + case ARRSPEC_4BYTES: return ".4b"; + case ARRSPEC_1HALF: return ".1h"; + case ARRSPEC_1BYTE: return ".1b"; + default: return ""; + } +} + +const char *get_arrspec_str_truncated(ArrangementSpec arrspec) +{ + switch(arrspec) { + case ARRSPEC_FULL: return ".q"; + case ARRSPEC_2DOUBLES: return ".d"; + case ARRSPEC_4SINGLES: return ".s"; + case ARRSPEC_8HALVES: return ".h"; + case ARRSPEC_16BYTES: return ".b"; + case ARRSPEC_1DOUBLE: return ".d"; + case ARRSPEC_2SINGLES: return ".s"; + case ARRSPEC_4HALVES: return ".h"; + case ARRSPEC_8BYTES: return ".b"; + case ARRSPEC_1SINGLE: return ".s"; + case ARRSPEC_2HALVES: return ".h"; + case ARRSPEC_4BYTES: return ".4b"; // not an error, UDOT_asimdelem_D and SDOT_asimdelem_D use this + case ARRSPEC_1HALF: return ".h"; + case ARRSPEC_1BYTE: return ".b"; + default: return ""; + } +} + +const char *get_register_arrspec(Register reg, const InstructionOperand *operand) +{ + if(operand->arrSpec == ARRSPEC_NONE) + return ""; + + bool is_simd = reg >= REG_V0 && reg <= REG_V31; + bool is_sve = reg >= REG_Z0 && reg <= REG_Z31; + bool is_pred = reg >= REG_P0 && reg <= REG_P31; + + if(!is_simd && !is_sve && !is_pred) + return ""; + + if(operand->laneUsed || is_sve || is_pred) + return get_arrspec_str_truncated(operand->arrSpec); + + return get_arrspec_str(operand->arrSpec); +} + +int get_register_full(Register reg, const InstructionOperand *operand, char *result) +{ + strcpy(result, get_register_name(reg)); + if(result[0] == '\0') + return -1; + + strcat(result, get_register_arrspec(reg, operand)); + + return 0; +} + +//----------------------------------------------------------------------------- +// miscellany to string +//----------------------------------------------------------------------------- + +uint32_t get_implementation_specific(const InstructionOperand *operand, char *outBuffer, uint32_t outBufferSize) +{ + return snprintf(outBuffer, + outBufferSize, + "s%d_%d_c%d_c%d_%d", + operand->implspec[0], + operand->implspec[1], + operand->implspec[2], + operand->implspec[3], + operand->implspec[4]) >= outBufferSize; +} + +const char *get_operation(const Instruction *inst) +{ + return operation_to_str(inst->operation); +} + +static const char *ConditionString[] = { + "eq", "ne", "cs", "cc", + "mi", "pl", "vs", "vc", + "hi", "ls", "ge", "lt", + "gt", "le", "al", "nv" +}; + +const char *get_condition(Condition cond) +{ + if (cond < 0 || cond >= END_CONDITION) + return NULL; + + return ConditionString[cond]; +} + +static const char *ShiftString[] = { + "NONE", "lsl", "lsr", "asr", + "ror", "uxtw", "sxtw", "sxtx", + "uxtx", "sxtb", "sxth", "uxth", + "uxtb", "msl" +}; + +const char *get_shift(ShiftType shift) +{ + if (shift <= ShiftType_NONE || shift >= ShiftType_END) + return NULL; + + return ShiftString[shift]; +} + +//----------------------------------------------------------------------------- +// operand processing helpers +//----------------------------------------------------------------------------- + +static inline uint32_t get_shifted_register( + const InstructionOperand *operand, + uint32_t registerNumber, + char *outBuffer, + uint32_t outBufferSize) +{ + char immBuff[32] = {0}; + char shiftBuff[64] = {0}; + + char reg[16]; + if(get_register_full(operand->reg[registerNumber], operand, reg)) + return FAILED_TO_DISASSEMBLE_REGISTER; + + if (operand->shiftType != ShiftType_NONE) + { + if (operand->shiftValueUsed != 0) + { + if (snprintf(immBuff, sizeof(immBuff), " #%#x", operand->shiftValue) >= sizeof(immBuff)) + { + return FAILED_TO_DISASSEMBLE_REGISTER; + } + } + const char *shiftStr = get_shift(operand->shiftType); + if (shiftStr == NULL) + return FAILED_TO_DISASSEMBLE_OPERAND; + snprintf( + shiftBuff, + sizeof(shiftBuff), + ", %s%s", + shiftStr, + immBuff); + } + if (snprintf(outBuffer, outBufferSize, "%s%s", reg, shiftBuff) < 0) + return FAILED_TO_DISASSEMBLE_REGISTER; + return DISASM_SUCCESS; +} + +uint32_t get_memory_operand( + const InstructionOperand *operand, + char *outBuffer, + uint32_t outBufferSize) +{ + char immBuff[64]= {0}; + char extendBuff[48] = {0}; + char paramBuff[32] = {0}; + + char reg0[16]={'\0'}, reg1[16]={'\0'}; + if(get_register_full(operand->reg[0], operand, reg0)) + return FAILED_TO_DISASSEMBLE_REGISTER; + + const char *sign = ""; + int64_t imm = operand->immediate; + if (operand->signedImm && (int64_t)imm < 0) + { + sign = "-"; + imm = -imm; + } + + switch (operand->operandClass) + { + case MEM_REG: + if (snprintf(outBuffer, outBufferSize, "[%s]", reg0) >= outBufferSize) + return FAILED_TO_DISASSEMBLE_OPERAND; + break; + + case MEM_PRE_IDX: + if (snprintf(outBuffer, outBufferSize, "[%s, #%s%#" PRIx64 "]!", reg0, sign, (uint64_t)imm) >= outBufferSize) + return FAILED_TO_DISASSEMBLE_OPERAND; + break; + + case MEM_POST_IDX: // [<reg>], <reg|imm> + if (operand->reg[1] != REG_NONE) { + if(get_register_full((Register)operand->reg[1], operand, reg1)) + return FAILED_TO_DISASSEMBLE_REGISTER; + + snprintf(paramBuff, sizeof(paramBuff), ", %s", reg1); + } + else if (snprintf(paramBuff, sizeof(paramBuff), ", #%s%#" PRIx64, sign, (uint64_t)imm) >= sizeof(paramBuff)) + return FAILED_TO_DISASSEMBLE_OPERAND; + + if (snprintf(outBuffer, outBufferSize, "[%s]%s", reg0, paramBuff) >= outBufferSize) + return FAILED_TO_DISASSEMBLE_OPERAND; + + break; + + case MEM_OFFSET: // [<reg> optional(imm)] + if (operand->immediate != 0) { + const char *mul_vl = operand->mul_vl ? ", mul vl" : ""; + if(snprintf(immBuff, sizeof(immBuff), ", #%s%#" PRIx64 "%s", sign, (uint64_t)imm, mul_vl) >= sizeof(immBuff)) { + return FAILED_TO_DISASSEMBLE_OPERAND; + } + } + + if (snprintf(outBuffer, outBufferSize, "[%s%s]", reg0, immBuff) >= outBufferSize) + return FAILED_TO_DISASSEMBLE_OPERAND; + break; + + case MEM_EXTENDED: + if(get_register_full(operand->reg[1], operand, reg1)) + return FAILED_TO_DISASSEMBLE_REGISTER; + + if (reg0[0] == '\0' || reg1[0] == '\0') { + return FAILED_TO_DISASSEMBLE_OPERAND; + } + + // immBuff, like "#0x0" + if (operand->shiftValueUsed) + if(snprintf(immBuff, sizeof(immBuff), " #%#x", operand->shiftValue) >= sizeof(immBuff)) + return FAILED_TO_DISASSEMBLE_OPERAND; + + // extendBuff, like "lsl #0x0" + if (operand->shiftType != ShiftType_NONE) + { + if (snprintf(extendBuff, sizeof(extendBuff), ", %s%s", + get_shift(operand->shiftType), immBuff) >= sizeof(extendBuff)) + { + return FAILED_TO_DISASSEMBLE_OPERAND; + } + } + + // together, like "[x24, x30, lsl #0x0]" + if (snprintf(outBuffer, outBufferSize, "[%s, %s%s]", reg0, reg1, extendBuff) >= outBufferSize) + return FAILED_TO_DISASSEMBLE_OPERAND; + + break; + default: + return NOT_MEMORY_OPERAND; + } + return DISASM_SUCCESS; +} + +uint32_t get_register(const InstructionOperand *operand, uint32_t registerNumber, char *outBuffer, uint32_t outBufferSize) +{ + /* 1) handle system registers */ + if(operand->operandClass == SYS_REG) + { + if (snprintf(outBuffer, outBufferSize, "%s", + get_system_register_name(operand->sysreg)) >= outBufferSize) + return FAILED_TO_DISASSEMBLE_REGISTER; + return 0; + } + + if(operand->operandClass != REG && operand->operandClass != MULTI_REG) + return OPERAND_IS_NOT_REGISTER; + + /* 2) handle shifted registers */ + if (operand->shiftType != ShiftType_NONE) + { + return get_shifted_register(operand, registerNumber, outBuffer, outBufferSize); + } + + char reg_buf[16]; + if(get_register_full(operand->reg[registerNumber], operand, reg_buf)) + return FAILED_TO_DISASSEMBLE_REGISTER; + + /* 3) handle predicate registers */ + if(operand->operandClass == REG && operand->pred_qual && operand->reg[0] >= REG_P0 && operand->reg[0] <= REG_P31) + { + if(snprintf(outBuffer, outBufferSize, "%s/%c", reg_buf, operand->pred_qual) >= outBufferSize) + return FAILED_TO_DISASSEMBLE_REGISTER; + return 0; + } + + /* 4) handle other registers */ + char index[32] = {0}; + if(operand->operandClass == REG && operand->laneUsed) + snprintf(index, sizeof(index), "[%u]", operand->lane); + + if(snprintf(outBuffer, outBufferSize, "%s%s", reg_buf, index) >= outBufferSize) + return FAILED_TO_DISASSEMBLE_REGISTER; + + return 0; +} + +uint32_t get_multireg_operand(const InstructionOperand *operand, char *result, uint32_t result_sz) +{ + char lane_str[32] = {0}; + char reg_str[4][32]; + uint32_t elem_n; + int rc; + memset(®_str, 0, sizeof(reg_str)); + + for (elem_n = 0; elem_n < 4 && operand->reg[elem_n] != REG_NONE; elem_n++) + if (get_register(operand, elem_n, reg_str[elem_n], 32) != 0) + return FAILED_TO_DISASSEMBLE_OPERAND; + + if(operand->laneUsed) + snprintf(lane_str, sizeof(lane_str), "[%d]", operand->lane); + + switch (elem_n) + { + case 1: + rc = snprintf(result, result_sz, "{%s}%s", + reg_str[0], lane_str); + break; + case 2: + rc = snprintf(result, result_sz, "{%s, %s}%s", + reg_str[0], reg_str[1], lane_str); + break; + case 3: + rc = snprintf(result, result_sz, "{%s, %s, %s}%s", + reg_str[0], reg_str[1], reg_str[2], lane_str); + break; + case 4: + rc = snprintf(result, result_sz, "{%s, %s, %s, %s}%s", + reg_str[0], reg_str[1], reg_str[2], reg_str[3], lane_str); + break; + default: + return FAILED_TO_DISASSEMBLE_OPERAND; + } + + return rc < 0 ? FAILED_TO_DISASSEMBLE_OPERAND : DISASM_SUCCESS; +} + +uint32_t get_shifted_immediate(const InstructionOperand *instructionOperand, char *outBuffer, uint32_t outBufferSize, uint32_t type) +{ + char shiftBuff[48] = {0}; + char immBuff[32] = {0}; + const char *sign = ""; + if (instructionOperand == NULL) + return FAILED_TO_DISASSEMBLE_OPERAND; + + uint64_t imm = instructionOperand->immediate; + if (instructionOperand->signedImm == 1 && ((int64_t)imm) < 0) + { + sign = "-"; + imm = -(int64_t)imm; + } + if (instructionOperand->shiftType != ShiftType_NONE) + { + if (instructionOperand->shiftValueUsed != 0) + { + if (snprintf(immBuff, sizeof(immBuff), " #%#x", instructionOperand->shiftValue) >= sizeof(immBuff)) + { + return FAILED_TO_DISASSEMBLE_REGISTER; + } + } + const char *shiftStr = get_shift(instructionOperand->shiftType); + if (shiftStr == NULL) + return FAILED_TO_DISASSEMBLE_OPERAND; + snprintf( + shiftBuff, + sizeof(shiftBuff), + ", %s%s", + shiftStr, + immBuff); + } + if (type == FIMM32) + { + float f = *(const float*)&instructionOperand->immediate; + if (snprintf(outBuffer, outBufferSize, "#%.08f%s", f, shiftBuff) >= outBufferSize) + return FAILED_TO_DISASSEMBLE_OPERAND; + } + else if (type == IMM32) + { + if (snprintf(outBuffer, outBufferSize, "#%s%#x%s", sign, (uint32_t)imm, shiftBuff) >= outBufferSize) + return FAILED_TO_DISASSEMBLE_OPERAND; + } + else if (type == LABEL) + { + if (snprintf(outBuffer, outBufferSize, "0x%" PRIx64, (uint64_t)imm) >= outBufferSize) + return FAILED_TO_DISASSEMBLE_OPERAND; + } + else if (type == STR_IMM) + { + if (snprintf(outBuffer, outBufferSize, "%s #0x%" PRIx64, instructionOperand->name, (uint64_t)imm) >= outBufferSize) + return FAILED_TO_DISASSEMBLE_OPERAND; + } + else + { + if (snprintf(outBuffer, outBufferSize, "#%s%#" PRIx64 "%s", + sign, + imm, + shiftBuff) >= outBufferSize) + return FAILED_TO_DISASSEMBLE_OPERAND; + } + return DISASM_SUCCESS; +} + +uint32_t get_sme_tile(const InstructionOperand *operand, char *outBuffer, uint32_t outBufferSize) +{ + char base_offset[32] = {'\0'}; + if(operand->reg[0] != REG_NONE) { + if(operand->arrSpec == ARRSPEC_FULL) + snprintf(base_offset, sizeof(base_offset), "[%s]", get_register_name(operand->reg[0])); + else + snprintf(base_offset, sizeof(base_offset), "[%s, #%llu]", get_register_name(operand->reg[0]), operand->immediate); + } + + char *slice = ""; + if(operand->slice == SLICE_HORIZONTAL) + slice = "H"; + else if(operand->slice == SLICE_VERTICAL) + slice = "V"; + + if(snprintf(outBuffer, outBufferSize, "Z%d%s%s%s", + operand->tile, + slice, + get_arrspec_str_truncated(operand->arrSpec), + base_offset + ) >= outBufferSize) + return FAILED_TO_DISASSEMBLE_OPERAND; + + return DISASM_SUCCESS; +} + +uint32_t get_indexed_element(const InstructionOperand *operand, char *outBuffer, uint32_t outBufferSize) +{ + // make the "{, #<imm>}" + char optional_comma_and[32]; + if(operand->immediate) + if(snprintf(optional_comma_and, 32, ", #%llu", operand->immediate) >= 32) + return FAILED_TO_DISASSEMBLE_OPERAND; + + // <Pn>.<T>[<Wm>{, #<imm>}] + if(snprintf(outBuffer, outBufferSize, "%s%s[%s%s]", + get_register_name(operand->reg[0]), + get_arrspec_str_truncated(operand->arrSpec), + get_register_name(operand->reg[1]), + optional_comma_and + ) >= outBufferSize) + return FAILED_TO_DISASSEMBLE_OPERAND; + + return DISASM_SUCCESS; +} + +uint32_t get_accum_array(const InstructionOperand *operand, char *outBuffer, uint32_t outBufferSize) +{ + if(snprintf(outBuffer, outBufferSize, "ZA[%s, #%llu]", + get_register_name(operand->reg[0]), operand->immediate + ) >= outBufferSize) + return FAILED_TO_DISASSEMBLE_OPERAND; + + return DISASM_SUCCESS; +} + +//----------------------------------------------------------------------------- +// disassemble (decoded Instruction -> string) +//----------------------------------------------------------------------------- + +int aarch64_disassemble(Instruction *instruction, char *buf, size_t buf_sz) +{ + char operandStrings[MAX_OPERANDS][130]; + char tmpOperandString[128]; + const char *operand = tmpOperandString; + if (instruction == NULL || buf_sz == 0 || buf == NULL) + return INVALID_ARGUMENTS; + + memset(operandStrings, 0, sizeof(operandStrings)); + const char *operation = get_operation(instruction); + if (operation == NULL) + return FAILED_TO_DISASSEMBLE_OPERATION; + + for(int i=0; i<MAX_OPERANDS; i++) + memset(&(operandStrings[i][0]), 0, 128); + + for(int i=0; i<MAX_OPERANDS && instruction->operands[i].operandClass != NONE; i++) + { + switch (instruction->operands[i].operandClass) + { + case CONDITION: + if (snprintf(tmpOperandString, sizeof(tmpOperandString), "%s", + get_condition((Condition)instruction->operands[i].cond)) >= sizeof(tmpOperandString)) + return FAILED_TO_DISASSEMBLE_OPERAND; + operand = tmpOperandString; + break; + case FIMM32: + case IMM32: + case IMM64: + case LABEL: + case STR_IMM: + if (get_shifted_immediate( + &instruction->operands[i], + tmpOperandString, + sizeof(tmpOperandString), + instruction->operands[i].operandClass) != DISASM_SUCCESS) + return FAILED_TO_DISASSEMBLE_OPERAND; + operand = tmpOperandString; + break; + case REG: + if (get_register( + &instruction->operands[i], + 0, + tmpOperandString, + sizeof(tmpOperandString)) != DISASM_SUCCESS) + return FAILED_TO_DISASSEMBLE_OPERAND; + operand = tmpOperandString; + break; + case SYS_REG: + operand = get_system_register_name(instruction->operands[i].sysreg); + if (operand == NULL) + { + return FAILED_TO_DISASSEMBLE_OPERAND; + } + break; + case MULTI_REG: + if (get_multireg_operand( + &instruction->operands[i], + tmpOperandString, + sizeof(tmpOperandString)) != DISASM_SUCCESS) + { + return FAILED_TO_DISASSEMBLE_OPERAND; + } + operand = tmpOperandString; + break; + case IMPLEMENTATION_SPECIFIC: + if (get_implementation_specific( + &instruction->operands[i], + tmpOperandString, + sizeof(tmpOperandString)) != DISASM_SUCCESS) + { + return FAILED_TO_DISASSEMBLE_OPERAND; + } + operand = tmpOperandString; + break; + case MEM_REG: + case MEM_OFFSET: + case MEM_EXTENDED: + case MEM_PRE_IDX: + case MEM_POST_IDX: + if (get_memory_operand(&instruction->operands[i], + tmpOperandString, + sizeof(tmpOperandString)) != DISASM_SUCCESS) + return FAILED_TO_DISASSEMBLE_OPERAND; + operand = tmpOperandString; + break; + case SME_TILE: + if(get_sme_tile(&instruction->operands[i], + tmpOperandString, + sizeof(tmpOperandString)) != DISASM_SUCCESS) + return FAILED_TO_DISASSEMBLE_OPERAND; + operand = tmpOperandString; + break; + case INDEXED_ELEMENT: + if(get_indexed_element(&instruction->operands[i], + tmpOperandString, + sizeof(tmpOperandString)) != DISASM_SUCCESS) + return FAILED_TO_DISASSEMBLE_OPERAND; + operand = tmpOperandString; + break; + case ACCUM_ARRAY: + if(get_accum_array(&instruction->operands[i], + tmpOperandString, + sizeof(tmpOperandString)) != DISASM_SUCCESS) + return FAILED_TO_DISASSEMBLE_OPERAND; + operand = tmpOperandString; + break; + case NAME: + operand = instruction->operands[i].name; + break; + case NONE: + break; + } + snprintf(operandStrings[i], sizeof(operandStrings[i]), i==0?"\t%s":", %s", operand); + } + memset(buf, 0, buf_sz); + if (snprintf(buf, buf_sz, "%s%s%s%s%s%s", + get_operation(instruction), + operandStrings[0], + operandStrings[1], + operandStrings[2], + operandStrings[3], + operandStrings[4]) >= buf_sz) + return OUTPUT_BUFFER_TOO_SMALL; + return DISASM_SUCCESS; +} + +void print_instruction(Instruction *instr) +{ + //printf("print_instruction (TODO)\n"); +} |
