Asm

/* $Id$ */
/* Copyright (c) 2011-2018 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS Devel Asm */
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <System.h>
/* arm */
/* private */
/* types */
struct _AsmArchPlugin
{
AsmArchPluginHelper * helper;
};
/* prototypes */
/* plug-in */
static AsmArchPlugin * _arm_init(AsmArchPluginHelper * helper);
static void _arm_destroy(AsmArchPlugin * plugin);
static int _arm_decode(AsmArchPlugin * plugin, AsmArchInstructionCall * call);
static int _arm_encode(AsmArchPlugin * plugin,
AsmArchPrefix const * prefix,
AsmArchInstruction const * instruction,
AsmArchInstructionCall const * call);
/* functions */
/* plug-in */
/* arm_init */
static AsmArchPlugin * _arm_init(AsmArchPluginHelper * helper)
{
AsmArchPlugin * plugin;
if((plugin = object_new(sizeof(*plugin))) == NULL)
return NULL;
plugin->helper = helper;
return plugin;
}
/* arm_destroy */
static void _arm_destroy(AsmArchPlugin * plugin)
{
object_delete(plugin);
}
/* arm_decode */
static void _decode_reg_reg_dreg(AsmArchPlugin * plugin,
AsmArchInstructionCall * call, uint32_t opcode);
static void _decode_reg_reg_reg(AsmArchPlugin * plugin,
AsmArchInstructionCall * call, uint32_t opcode);
static void _decode_reg_reg_u12(AsmArchPlugin * plugin,
AsmArchInstructionCall * call, uint32_t opcode);
static void _decode_s24(AsmArchInstructionCall * call, uint32_t opcode);
static void _decode_u24(AsmArchInstructionCall * call, uint32_t opcode);
static void _decode_u4_u4_reg(AsmArchPlugin * plugin,
AsmArchInstructionCall * call, uint32_t opcode);
static int _decode_unknown(AsmArchInstructionCall * call, uint32_t opcode);
static int _arm_decode(AsmArchPlugin * plugin, AsmArchInstructionCall * call)
{
AsmArchPluginHelper * helper = plugin->helper;
uint32_t opcode;
uint32_t op;
AsmArchInstruction const * ai = NULL;
/* read 4 bytes in the proper endian */
if(helper->read(helper->arch, &opcode, sizeof(opcode))
!= sizeof(opcode))
return -1;
#if 1
/* FIXME apply as relevant */
opcode = _htob32(opcode);
#endif
/* lookup the instruction */
/* FIXME decode everything in the proper order */
/* opcode bits 27, 26, 25 and 24 set */
if((op = (opcode & OPSI(0x0))) == OPSI(0x0))
{
ai = helper->get_instruction_by_opcode(helper->arch, 32,
opcode & OPSI(0xf));
_decode_u24(call, opcode);
}
/* opcode bits 27, 26, 25 set */
else if((op = (opcode & OPCDO(0x0))) == OPCDO(0x0))
{
ai = helper->get_instruction_by_opcode(helper->arch, 32,
opcode & OPCDO(0xf));
_decode_u4_u4_reg(plugin, call, opcode);
}
/* opcode bits 27, 25, 24 set */
else if((op = (opcode & OPBL(0x0))) == OPBL(0x0))
{
ai = helper->get_instruction_by_opcode(helper->arch, 32,
opcode & OPBL(0xf));
_decode_s24(call, opcode);
}
/* opcode bits 27, 25 set */
else if((op = (opcode & OPB(0x0))) == OPB(0x0))
{
ai = helper->get_instruction_by_opcode(helper->arch, 32,
opcode & OPB(0xf));
_decode_s24(call, opcode);
}
/* opcode bits 26, 25, 22, 20 */
else if((op = (opcode & OPSDTLB(0x0))) == OPSDTLB(0x0))
ai = helper->get_instruction_by_opcode(helper->arch, 32,
opcode & OPSDTLB(0xf));
/* opcode bits 26, 25, 22 set */
else if((op = (opcode & OPSDTSB(0x0))) == OPSDTSB(0x0))
ai = helper->get_instruction_by_opcode(helper->arch, 32,
opcode & OPSDTSB(0xf));
/* opcode bits 26, 25, 20 set */
else if((op = (opcode & OPSDTL(0x0))) == OPSDTL(0x0))
ai = helper->get_instruction_by_opcode(helper->arch, 32,
opcode & OPSDTL(0xf));
/* opcode bits 26, 25 set */
else if((op = (opcode & OPSDTS(0x0))) == OPSDTS(0x0))
ai = helper->get_instruction_by_opcode(helper->arch, 32,
opcode & OPSDTS(0xf));
/* opcode bits 25, 20 set */
else if((op = (opcode & OPDPIS(0x0, 0x0))) == OPDPIS(0x0, 0x0))
{
ai = helper->get_instruction_by_opcode(helper->arch, 32,
opcode & OPDPIS(0xf, 0xf));
_decode_reg_reg_u12(plugin, call, opcode);
}
/* bit 25 is set */
else if((op = (opcode & OPDPI(0x0, 0x0))) == OPDPI(0x0, 0x0))
{
ai = helper->get_instruction_by_opcode(helper->arch, 32,
opcode & OPDPI(0xf, 0xf));
_decode_reg_reg_u12(plugin, call, opcode);
}
/* opcode bits 24, 22 set */
else if((op = (opcode & OPSDSB(0x0))) == OPSDSB(0x0))
{
ai = helper->get_instruction_by_opcode(helper->arch, 32,
opcode & OPSDSB(0xf));
_decode_reg_reg_dreg(plugin, call, opcode);
}
/* bit 24 is set */
else if((op = (opcode & OPPTI(0x0))) == OPPTI(0x0))
ai = helper->get_instruction_by_opcode(helper->arch, 32,
opcode & OPPTI(0xf));
/* bit 24 is set */
else if((op = (opcode & OPPT(0x0))) == OPPT(0x0))
ai = helper->get_instruction_by_opcode(helper->arch, 32,
opcode & OPPT(0xf));
/* bit 24 is set */
else if((op = (opcode & OPSDS(0x0))) == OPSDS(0x0))
{
ai = helper->get_instruction_by_opcode(helper->arch, 32,
opcode & OPSDS(0xf));
_decode_reg_reg_dreg(plugin, call, opcode);
}
/* opcode bits 21, 20, 8 and 4 set */
else if((op = (opcode & OPMULAS(0x0))) == OPMULAS(0x0))
ai = helper->get_instruction_by_opcode(helper->arch, 32,
opcode & OPMULAS(0xf));
/* opcode bits 21, 18, 16-8, 4 set */
else if((op = (opcode & OPBX(0x0))) == OPBX(0x0))
ai = helper->get_instruction_by_opcode(helper->arch, 32,
opcode & OPBX(0xf));
/* opcode bits 21, 8 and 4 set */
else if((op = (opcode & OPMULA(0x0))) == OPMULA(0x0))
ai = helper->get_instruction_by_opcode(helper->arch, 32,
opcode & OPMULA(0xf));
/* opcode bits 20, 8 and 4 set */
else if((op = (opcode & OPMULS(0x0))) == OPMULS(0x0))
ai = helper->get_instruction_by_opcode(helper->arch, 32,
opcode & OPMULS(0xf));
/* opcode bit 20 set */
else if((op = (opcode & OPDPS(0x0, 0x0))) == OPDPS(0x0, 0x0))
{
ai = helper->get_instruction_by_opcode(helper->arch, 32,
opcode & OPDPS(0xf, 0xf));
_decode_reg_reg_reg(plugin, call, opcode);
}
/* opcode bits 8 and 4 set */
else if((op = (opcode & OPMUL(0x0))) == OPMUL(0x0))
{
ai = helper->get_instruction_by_opcode(helper->arch, 32,
opcode & OPMUL(0xf));
_decode_reg_reg_reg(plugin, call, opcode);
}
/* no opcode bits set */
else if((op = (opcode & OPDP(0x0, 0x0))) == OPDP(0x0, 0x0))
{
ai = helper->get_instruction_by_opcode(helper->arch, 32,
opcode & OPDP(0xf, 0xf));
_decode_reg_reg_reg(plugin, call, opcode);
}
if(ai == NULL)
/* unknown instruction */
return _decode_unknown(call, opcode);
call->name = ai->name;
call->operands_cnt = 0;
if((call->operands[0].definition = ai->op1) != AOT_NONE)
call->operands_cnt++;
if((call->operands[1].definition = ai->op2) != AOT_NONE)
call->operands_cnt++;
if((call->operands[2].definition = ai->op3) != AOT_NONE)
call->operands_cnt++;
return 0;
}
static void _decode_reg_reg_dreg(AsmArchPlugin * plugin,
AsmArchInstructionCall * call, uint32_t opcode)
{
AsmArchPluginHelper * helper = plugin->helper;
AsmArchRegister const * ar;
if((ar = helper->get_register_by_id_size(helper->arch,
(opcode >> 12) & 0xf, 32)) != NULL)
call->operands[0].value._register.name = ar->name;
if((ar = helper->get_register_by_id_size(helper->arch,
(opcode >> 16) & 0xf, 32)) != NULL)
call->operands[1].value._register.name = ar->name;
if((ar = helper->get_register_by_id_size(helper->arch,
opcode & 0xf, 32)) != NULL)
call->operands[2].value.dregister.name = ar->name;
}
static void _decode_reg_reg_reg(AsmArchPlugin * plugin,
AsmArchInstructionCall * call, uint32_t opcode)
{
AsmArchPluginHelper * helper = plugin->helper;
AsmArchRegister const * ar;
if((ar = helper->get_register_by_id_size(helper->arch,
(opcode >> 12) & 0xf, 32)) != NULL)
call->operands[0].value._register.name = ar->name;
if((ar = helper->get_register_by_id_size(helper->arch,
(opcode >> 16) & 0xf, 32)) != NULL)
call->operands[1].value._register.name = ar->name;
if((ar = helper->get_register_by_id_size(helper->arch,
opcode & 0xf, 32)) != NULL)
call->operands[2].value._register.name = ar->name;
}
static void _decode_reg_reg_u12(AsmArchPlugin * plugin,
AsmArchInstructionCall * call, uint32_t opcode)
{
AsmArchPluginHelper * helper = plugin->helper;
AsmArchRegister const * ar;
if((ar = helper->get_register_by_id_size(helper->arch,
(opcode >> 12) & 0xf, 32)) != NULL)
call->operands[0].value._register.name = ar->name;
if((ar = helper->get_register_by_id_size(helper->arch,
(opcode >> 16) & 0xf, 32)) != NULL)
call->operands[1].value._register.name = ar->name;
call->operands[2].value.immediate.value = opcode & 0xfff;
}
static void _decode_s24(AsmArchInstructionCall * call, uint32_t opcode)
{
call->operands[0].value.immediate.value = opcode & 0x00ffffff;
/* FIXME properly restore the sign */
if(opcode & 0x00800000)
call->operands[0].value.immediate.negative = 1;
}
static void _decode_u24(AsmArchInstructionCall * call, uint32_t opcode)
{
call->operands[0].value.immediate.value = opcode & 0x00ffffff;
}
static void _decode_u4_u4_reg(AsmArchPlugin * plugin,
AsmArchInstructionCall * call, uint32_t opcode)
{
AsmArchPluginHelper * helper = plugin->helper;
AsmArchRegister const * ar;
/* FIXME implement u4 and u4 */
if((ar = helper->get_register_by_id_size(helper->arch,
(opcode >> 12) & 0xf, 32)) != NULL)
call->operands[2].value._register.name = ar->name;
}
static int _decode_unknown(AsmArchInstructionCall * call, uint32_t opcode)
{
call->name = "dw";
call->operands[0].definition = AO_IMMEDIATE(0, 32, 0);
call->operands[0].value.immediate.value = opcode;
call->operands_cnt = 1;
return 0;
}
/* arm_encode */
static int _arm_encode(AsmArchPlugin * plugin,
AsmArchPrefix const * prefix,
AsmArchInstruction const * instruction,
AsmArchInstructionCall const * call)
{
AsmArchPluginHelper * helper = plugin->helper;
uint32_t opcode = instruction->opcode;
AsmArchRegister const * ar;
char const * p;
if(prefix != NULL)
return -error_set_code(1, "%s: %s",
helper->get_filename(helper->arch),
"Prefixes not supported for this architecture");
switch(opcode & 0x0fffffff) /* ignore condition code */
{
/* branch, branch with link */
case OPB(0): /* b */
case OPBL(0): /* bl */
/* FIXME properly keep the sign */
opcode |= (call->operands[0].value.immediate.value
& 0x00ffffff);
break;
/* branch and exchange */
case OPBX(0): /* bx */
/* first operand, Rn */
p = call->operands[0].value._register.name;
if((ar = helper->get_register_by_name_size(helper->arch,
p, 32)) == NULL)
return -1;
opcode |= ar->id;
break;
/* data processing */
case OPDP(0, and): /* and */
case OPDP(0, eor): /* eor */
case OPDP(0, sub): /* sub */
case OPDP(0, rsb): /* rsb */
case OPDP(0, add): /* add */
case OPDP(0, adc): /* adc */
case OPDP(0, sbc): /* sbc */
case OPDP(0, rsc): /* rsc */
case OPDP(0, orr): /* orr */
case OPDP(0, bic): /* bic */
case OPDPS(0, and): /* ands */
case OPDPS(0, eor): /* eors */
case OPDPS(0, sub): /* subs */
case OPDPS(0, rsb): /* rsbs */
case OPDPS(0, add): /* adds */
case OPDPS(0, adc): /* adcs */
case OPDPS(0, sbc): /* sbcs */
case OPDPS(0, rsc): /* rscs */
case OPDPS(0, orr): /* orrs */
case OPDPS(0, bic): /* bics */
/* first operand, Rd */
p = call->operands[0].value._register.name;
if((ar = helper->get_register_by_name_size(helper->arch,
p, 32)) == NULL)
return -1;
opcode |= (ar->id << 12);
/* second operand, Rn */
p = call->operands[1].value._register.name;
if((ar = helper->get_register_by_name_size(helper->arch,
p, 32)) == NULL)
return -1;
opcode |= (ar->id << 16);
/* third operand, Rm */
p = call->operands[2].value._register.name;
if((ar = helper->get_register_by_name_size(helper->arch,
p, 32)) == NULL)
return -1;
opcode |= ar->id;
break;
case OPDPI(0, and): /* and (immediate) */
case OPDPI(0, eor): /* eor (immediate) */
case OPDPI(0, sub): /* sub (immediate) */
case OPDPI(0, rsb): /* rsb (immediate) */
case OPDPI(0, add): /* add (immediate) */
case OPDPI(0, adc): /* adc (immediate) */
case OPDPI(0, sbc): /* sbc (immediate) */
case OPDPI(0, rsc): /* rsc (immediate) */
case OPDPI(0, orr): /* orr (immediate) */
case OPDPI(0, bic): /* bic (immediate) */
case OPDPIS(0, and): /* ands (immediate) */
case OPDPIS(0, eor): /* eors (immediate) */
case OPDPIS(0, sub): /* subs (immediate) */
case OPDPIS(0, rsb): /* rsbs (immediate) */
case OPDPIS(0, add): /* adds (immediate) */
case OPDPIS(0, adc): /* adcs (immediate) */
case OPDPIS(0, sbc): /* sbcs (immediate) */
case OPDPIS(0, rsc): /* rscs (immediate) */
case OPDPIS(0, orr): /* orrs (immediate) */
case OPDPIS(0, bic): /* bics (immediate) */
/* first operand, Rd */
p = call->operands[0].value._register.name;
if((ar = helper->get_register_by_name_size(helper->arch,
p, 32)) == NULL)
return -1;
opcode |= (ar->id << 12);
/* second operand, Rn */
p = call->operands[1].value._register.name;
if((ar = helper->get_register_by_name_size(helper->arch,
p, 32)) == NULL)
return -1;
opcode |= (ar->id << 16);
/* third operand */
opcode |= call->operands[2].value.immediate.value;
break;
case OPDP(0, tst): /* tst */
case OPDP(0, teq): /* teq */
case OPDP(0, cmp): /* cmp */
case OPDP(0, cmn): /* cmn */
case OPDPS(0, tst): /* tsts */
case OPDPS(0, teq): /* teqs */
case OPDPS(0, cmp): /* cmps */
case OPDPS(0, cmn): /* cmns */
/* first operand, Rn */
p = call->operands[0].value._register.name;
if((ar = helper->get_register_by_name_size(helper->arch,
p, 32)) == NULL)
return -1;
opcode |= (ar->id << 16);
/* second operand, Rm */
p = call->operands[1].value._register.name;
if((ar = helper->get_register_by_name_size(helper->arch,
p, 32)) == NULL)
return -1;
opcode |= ar->id;
break;
case OPDPI(0, tst): /* tst (immediate) */
case OPDPI(0, teq): /* teq (immediate) */
case OPDPI(0, cmp): /* cmp (immediate) */
case OPDPI(0, cmn): /* cmn (immediate) */
case OPDPIS(0, tst): /* tsts (immediate) */
case OPDPIS(0, teq): /* teqs (immediate) */
case OPDPIS(0, cmp): /* cmps (immediate) */
case OPDPIS(0, cmn): /* cmns (immediate) */
/* first operand, Rn */
p = call->operands[0].value._register.name;
if((ar = helper->get_register_by_name_size(helper->arch,
p, 32)) == NULL)
return -1;
opcode |= (ar->id << 16);
/* second operand */
opcode |= call->operands[1].value.immediate.value;
break;
case OPDP(0, mov): /* mov */
case OPDPS(0, mov): /* movs */
case OPDP(0, mvn): /* mvn */
case OPDPS(0, mvn): /* mvns */
/* take care of nop */
if(call->operands_cnt == 0)
break;
/* first operand, Rd */
p = call->operands[0].value._register.name;
if((ar = helper->get_register_by_name_size(helper->arch,
p, 32)) == NULL)
return -1;
/* second operand, Rm */
opcode |= (ar->id << 12);
p = call->operands[1].value._register.name;
if((ar = helper->get_register_by_name_size(helper->arch,
p, 32)) == NULL)
return -1;
opcode |= ar->id;
break;
case OPDPI(0, mov): /* mov (immediate) */
case OPDPIS(0, mov): /* movs (immediate) */
case OPDPI(0, mvn): /* mvn (immediate) */
case OPDPIS(0, mvn): /* mvns (immediate) */
if(call->operands_cnt == 0) /* nop */
break;
/* first operand, Rd */
p = call->operands[0].value._register.name;
if((ar = helper->get_register_by_name_size(helper->arch,
p, 32)) == NULL)
return -1;
opcode |= (ar->id << 12);
/* second operand */
opcode |= call->operands[1].value.immediate.value;
break;
/* psr transfer */
case OPPT(0):
/* first operand, Rd */
p = call->operands[0].value._register.name;
if((ar = helper->get_register_by_name_size(helper->arch,
p, 32)) == NULL)
return -1;
opcode |= (ar->id << 12);
break;
case OPPTI(0):
/* second operand, Rm */
p = call->operands[1].value._register.name;
if((ar = helper->get_register_by_name_size(helper->arch,
p, 32)) == NULL)
return -1;
opcode |= ar->id;
break;
/* multiply and multiply-accumulate */
case OPMUL(0): /* mul */
case OPMULS(0): /* muls */
case OPMULA(0): /* mla */
case OPMULAS(0): /* mlas */
/* first operand, Rd */
p = call->operands[0].value._register.name;
if((ar = helper->get_register_by_name_size(helper->arch,
p, 32)) == NULL)
return -1;
opcode |= (ar->id << 16);
/* second operand, Rm */
p = call->operands[1].value._register.name;
if((ar = helper->get_register_by_name_size(helper->arch,
p, 32)) == NULL)
return -1;
opcode |= ar->id;
/* third operand, Rs */
p = call->operands[2].value._register.name;
if((ar = helper->get_register_by_name_size(helper->arch,
p, 32)) == NULL)
return -1;
opcode |= (ar->id << 8);
break;
/* single data transfer */
case OPSDTL(0): /* ldr */
case OPSDTS(0): /* str */
case OPSDTLB(0): /* ldrb */
case OPSDTSB(0): /* strb */
/* first operand, Rd */
p = call->operands[0].value._register.name;
if((ar = helper->get_register_by_name_size(helper->arch,
p, 32)) == NULL)
return -1;
opcode |= (ar->id << 12);
/* second operand, Rn */
p = call->operands[1].value._register.name;
if((ar = helper->get_register_by_name_size(helper->arch,
p, 32)) == NULL)
return -1;
opcode |= (ar->id << 16);
/* third operand, Rm */
p = call->operands[2].value._register.name;
if((ar = helper->get_register_by_name_size(helper->arch,
p, 32)) == NULL)
return -1;
opcode |= ar->id;
break;
/* block data transfer */
case OPBDTL(0): /* ldm */
case OPBDTS(0): /* stm */
/* first operand, Rn */
p = call->operands[0].value._register.name;
if((ar = helper->get_register_by_name_size(helper->arch,
p, 32)) == NULL)
return -1;
opcode |= (ar->id << 16);
/* second operand, register list */
opcode |= call->operands[1].value.immediate.value;
break;
/* single data swap */
case OPSDS(0):
case OPSDSB(0):
/* first operand, Rd */
p = call->operands[0].value._register.name;
if((ar = helper->get_register_by_name_size(helper->arch,
p, 32)) == NULL)
return -1;
opcode |= (ar->id << 12);
/* second operand, Rm */
p = call->operands[1].value._register.name;
if((ar = helper->get_register_by_name_size(helper->arch,
p, 32)) == NULL)
return -1;
opcode |= ar->id;
/* third operand, Rn */
p = call->operands[2].value._register.name;
if((ar = helper->get_register_by_name_size(helper->arch,
p, 32)) == NULL)
return -1;
opcode |= (ar->id << 16);
break;
/* software interrupt */
case OPSI(0):
opcode |= call->operands[0].value.immediate.value;
break;
/* coprocessor data operation */
case OPCDO(0):
/* first operand, coprocessor number */
opcode |= (call->operands[0].value.immediate.value
<< 8);
/* second operand, coprocessor operation code */
opcode |= (call->operands[1].value.immediate.value
<< 20);
/* third operand, CRd */
p = call->operands[2].value._register.name;
if((ar = helper->get_register_by_name_size(helper->arch,
p, 32)) == NULL)
return -1;
opcode |= (ar->id << 12);
break;
/* coprocessor data transfers */
case OPCDTL(0):
case OPCDTS(0):
/* first operand, coprocessor number */
opcode |= (call->operands[0].value.immediate.value
<< 8);
/* second operand, CRd */
p = call->operands[1].value._register.name;
if((ar = helper->get_register_by_name_size(helper->arch,
p, 32)) == NULL)
return -1;
opcode |= (ar->id << 12);
/* third operand, address */
opcode |= call->operands[2].value.immediate.value;
break;
/* coprocessor register transfers */
case OPCRTL(0):
case OPCRTS(0):
/* first operand, coprocessor number */
opcode |= (call->operands[0].value.immediate.value
<< 8);
/* second operand, opcode */
opcode |= (call->operands[1].value.immediate.value
<< 21);
/* third operand, Rd */
p = call->operands[2].value._register.name;
if((ar = helper->get_register_by_name_size(helper->arch,
p, 32)) == NULL)
return -1;
opcode |= (ar->id << 12);
/* FIXME implement */
break;
#if 1 /* FIXME really implement */
default:
break;
#endif
}
#if 1
/* FIXME apply as relevant */
opcode = _htob32(opcode);
#endif
if(helper->write(helper->arch, &opcode, sizeof(opcode))
!= sizeof(opcode))
return -1;
return 0;
}