603 lines
14 KiB
C
603 lines
14 KiB
C
/* $Id$ */
|
|
/* Copyright (c) 2011 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 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
|
|
|
|
#include <System.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include "Asm/arch.h"
|
|
#include "Asm/asm.h"
|
|
#include "arch.h"
|
|
#include "../config.h"
|
|
|
|
/* macros */
|
|
#ifndef abs
|
|
# define abs(a) ((a) >= 0 ? (a) : -(a))
|
|
#endif
|
|
#ifndef min
|
|
# define min(a, b) ((a) < (b) ? (a) : (b))
|
|
#endif
|
|
|
|
|
|
/* Arch */
|
|
/* private */
|
|
/* types */
|
|
struct _Arch
|
|
{
|
|
ArchPluginHelper helper;
|
|
Plugin * handle;
|
|
ArchPlugin * plugin;
|
|
size_t instructions_cnt;
|
|
size_t registers_cnt;
|
|
|
|
/* internal */
|
|
char const * filename;
|
|
FILE * fp;
|
|
char const * buffer;
|
|
size_t buffer_cnt;
|
|
size_t buffer_pos;
|
|
};
|
|
|
|
|
|
/* macros */
|
|
#define AI_GET_OPERAND_COUNT(ai) (((ai->op1 & AOM_TYPE) ? 1 : 0) + \
|
|
((ai->op2 & AOM_TYPE) ? 1 : 0) + \
|
|
((ai->op3 & AOM_TYPE) ? 1 : 0))
|
|
|
|
|
|
/* prototypes */
|
|
/* callbacks */
|
|
static char const * _arch_get_filename(Arch * arch);
|
|
static ssize_t _arch_read_buffer(Arch * arch, void * buf, size_t size);
|
|
static ssize_t _arch_write(Arch * arch, void const * buf, size_t size);
|
|
|
|
|
|
/* public */
|
|
/* functions */
|
|
/* arch_new */
|
|
Arch * arch_new(char const * name)
|
|
{
|
|
Arch * a;
|
|
Plugin * handle;
|
|
ArchPlugin * plugin;
|
|
|
|
if((handle = plugin_new(LIBDIR, PACKAGE, "arch", name)) == NULL)
|
|
return NULL;
|
|
if((plugin = plugin_lookup(handle, "arch_plugin")) == NULL)
|
|
{
|
|
plugin_delete(handle);
|
|
return NULL;
|
|
}
|
|
if((a = object_new(sizeof(*a))) == NULL)
|
|
{
|
|
object_delete(a);
|
|
plugin_delete(handle);
|
|
return NULL;
|
|
}
|
|
memset(&a->helper, 0, sizeof(a->helper));
|
|
a->handle = handle;
|
|
a->plugin = plugin;
|
|
a->instructions_cnt = 0;
|
|
if(a->plugin->instructions != NULL)
|
|
for(; a->plugin->instructions[a->instructions_cnt].name != NULL;
|
|
a->instructions_cnt++);
|
|
a->registers_cnt = 0;
|
|
if(a->plugin->registers != NULL)
|
|
for(; a->plugin->registers[a->registers_cnt].name != NULL;
|
|
a->registers_cnt++);
|
|
a->filename = NULL;
|
|
a->fp = NULL;
|
|
a->buffer = NULL;
|
|
a->buffer_cnt = 0;
|
|
a->buffer_pos = 0;
|
|
return a;
|
|
}
|
|
|
|
|
|
/* arch_delete */
|
|
void arch_delete(Arch * arch)
|
|
{
|
|
plugin_delete(arch->handle);
|
|
object_delete(arch);
|
|
}
|
|
|
|
|
|
/* accessors */
|
|
/* arch_get_description */
|
|
ArchDescription * arch_get_description(Arch * arch)
|
|
{
|
|
return arch->plugin->description;
|
|
}
|
|
|
|
|
|
/* arch_get_format */
|
|
char const * arch_get_format(Arch * arch)
|
|
{
|
|
if(arch->plugin->description != NULL
|
|
&& arch->plugin->description->format != NULL)
|
|
return arch->plugin->description->format;
|
|
return "elf";
|
|
}
|
|
|
|
|
|
/* arch_get_instruction */
|
|
ArchInstruction * arch_get_instruction(Arch * arch, size_t index)
|
|
{
|
|
if(index >= arch->instructions_cnt)
|
|
return NULL;
|
|
return &arch->plugin->instructions[index];
|
|
}
|
|
|
|
|
|
/* arch_get_instruction_by_name */
|
|
ArchInstruction * arch_get_instruction_by_name(Arch * arch, char const * name)
|
|
{
|
|
size_t i;
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "DEBUG: %s(arch, \"%s\")\n", __func__, name);
|
|
#endif
|
|
for(i = 0; i < arch->instructions_cnt; i++)
|
|
if(strcmp(arch->plugin->instructions[i].name, name) == 0)
|
|
return &arch->plugin->instructions[i];
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* arch_get_instruction_by_opcode */
|
|
ArchInstruction * arch_get_instruction_by_opcode(Arch * arch, uint8_t size,
|
|
uint32_t opcode)
|
|
{
|
|
size_t i;
|
|
ArchInstruction * ai;
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "DEBUG: %s(arch, %u, 0x%x)\n", __func__, size, opcode);
|
|
#endif
|
|
for(i = 0; i < arch->instructions_cnt; i++)
|
|
{
|
|
ai = &arch->plugin->instructions[i];
|
|
if(AO_GET_SIZE(ai->flags) != size)
|
|
continue;
|
|
if(ai->opcode == opcode)
|
|
return ai;
|
|
}
|
|
#if 0
|
|
if(arch->instructions[i].size == size
|
|
&& arch->instructions[i].opcode == opcode)
|
|
return &arch->instructions[i];
|
|
# if 0 /* XXX this is experimental and may not be adequate */
|
|
for(i = 0; i < arch->instructions_cnt; i++)
|
|
if(arch->instructions[i].size == 0
|
|
&& arch->instructions[i].op1size == size)
|
|
return &arch->instructions[i];
|
|
# endif
|
|
#endif
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* arch_get_instruction_by_call */
|
|
static int _call_operands(Arch * arch, ArchInstruction * instruction,
|
|
ArchInstructionCall * call);
|
|
static int _call_operands_constant(ArchOperandDefinition definition,
|
|
ArchOperand * operand);
|
|
static int _call_operands_dregister(Arch * arch,
|
|
ArchOperandDefinition definition, ArchOperand * operand);
|
|
static int _call_operands_immediate(ArchOperandDefinition definition,
|
|
ArchOperand * operand);
|
|
static int _call_operands_register(Arch * arch,
|
|
ArchOperandDefinition definition, ArchOperand * operand);
|
|
|
|
ArchInstruction * arch_get_instruction_by_call(Arch * arch,
|
|
ArchInstructionCall * call)
|
|
{
|
|
size_t i;
|
|
ArchInstruction * ai;
|
|
int found = 0;
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, call->name);
|
|
#endif
|
|
for(i = 0; i < arch->instructions_cnt; i++)
|
|
{
|
|
ai = &arch->plugin->instructions[i];
|
|
/* FIXME use a (sorted) hash table */
|
|
if(strcmp(ai->name, call->name) != 0)
|
|
continue;
|
|
found = 1;
|
|
if(_call_operands(arch, ai, call) == 0)
|
|
return ai;
|
|
}
|
|
error_set_code(1, "%s \"%s\"", found ? "Invalid arguments to"
|
|
: "Unknown instruction", call->name);
|
|
return NULL;
|
|
}
|
|
|
|
static int _call_operands(Arch * arch, ArchInstruction * instruction,
|
|
ArchInstructionCall * call)
|
|
{
|
|
size_t i;
|
|
ArchOperandDefinition definition;
|
|
ArchOperand * operand;
|
|
|
|
if(call->operands_cnt == 0 && AO_GET_TYPE(instruction->op1) != AOT_NONE)
|
|
return -1;
|
|
for(i = 0; i < call->operands_cnt; i++)
|
|
{
|
|
definition = (i == 0) ? instruction->op1 : ((i == 1)
|
|
? instruction->op2 : instruction->op3);
|
|
operand = &call->operands[i];
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "DEBUG: %s() operand %lu, type %u, type %u\n",
|
|
__func__, i, AO_GET_TYPE(definition),
|
|
AO_GET_TYPE(operand->type));
|
|
#endif
|
|
if(AO_GET_TYPE(definition) == AOT_CONSTANT)
|
|
{
|
|
if(operand->type != AOT_IMMEDIATE)
|
|
return -1;
|
|
}
|
|
else if(AO_GET_TYPE(definition) != operand->type)
|
|
return -1;
|
|
switch(AO_GET_TYPE(definition))
|
|
{
|
|
case AOT_CONSTANT:
|
|
if(_call_operands_constant(definition, operand)
|
|
!= 0)
|
|
return -1;
|
|
break;
|
|
case AOT_IMMEDIATE:
|
|
if(_call_operands_immediate(definition, operand)
|
|
!= 0)
|
|
return -1;
|
|
break;
|
|
case AOT_DREGISTER:
|
|
if(_call_operands_dregister(arch, definition,
|
|
operand) != 0)
|
|
return -1;
|
|
break;
|
|
case AOT_REGISTER:
|
|
if(_call_operands_register(arch, definition,
|
|
operand) != 0)
|
|
return -1;
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int _call_operands_constant(ArchOperandDefinition definition,
|
|
ArchOperand * operand)
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "DEBUG: %s() %u %lu\n", __func__,
|
|
AO_GET_VALUE(definition),
|
|
operand->value.immediate.value);
|
|
#endif
|
|
if(AO_GET_VALUE(definition) != operand->value.immediate.value)
|
|
return -1;
|
|
/* set this operand as a constant */
|
|
operand->type &= AOM_TYPE;
|
|
operand->type |= AOT_CONSTANT;
|
|
return 0;
|
|
}
|
|
|
|
static int _call_operands_dregister(Arch * arch,
|
|
ArchOperandDefinition definition, ArchOperand * operand)
|
|
{
|
|
uint64_t offset;
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "DEBUG: %s() %ld\n", __func__,
|
|
operand->value.dregister.offset);
|
|
#endif
|
|
if(_call_operands_register(arch, definition, operand) != 0)
|
|
return -1;
|
|
/* check if there is an offset applied */
|
|
if(operand->value.dregister.offset == 0)
|
|
return 0;
|
|
/* check if the offset fits */
|
|
offset = abs(operand->value.dregister.offset);
|
|
offset >>= AO_GET_DSIZE(definition);
|
|
if(offset > 0)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static int _call_operands_immediate(ArchOperandDefinition definition,
|
|
ArchOperand * operand)
|
|
{
|
|
uint64_t value;
|
|
uint32_t size;
|
|
|
|
/* check if the size fits */
|
|
value = operand->value.immediate.value;
|
|
#if 0 /* XXX ignore for now */
|
|
if((size = AO_GET_SIZE(definition)) > 0
|
|
&& AO_GET_FLAGS(definition) & AOF_SIGNED)
|
|
size--;
|
|
#else
|
|
size = AO_GET_SIZE(definition);
|
|
#endif
|
|
value >>= size;
|
|
if(value > 0)
|
|
return -1;
|
|
/* check if it is signed */
|
|
if(operand->value.immediate.negative
|
|
&& !(AO_GET_FLAGS(definition) & AOF_SIGNED))
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static int _call_operands_register(Arch * arch,
|
|
ArchOperandDefinition definition, ArchOperand * operand)
|
|
{
|
|
char const * name = operand->value._register.name;
|
|
ArchDescription * desc;
|
|
uint32_t size;
|
|
ArchRegister * ar;
|
|
|
|
/* obtain the size */
|
|
if((desc = arch->plugin->description) != NULL
|
|
&& desc->instruction_size != 0)
|
|
size = desc->instruction_size;
|
|
else
|
|
size = AO_GET_SIZE(definition);
|
|
/* check if it exists */
|
|
if((ar = arch_get_register_by_name_size(arch, name, size)) == NULL)
|
|
return -1;
|
|
/* for implicit instructions it must match */
|
|
if(AO_GET_FLAGS(definition) & AOF_IMPLICIT
|
|
&& AO_GET_VALUE(definition) != ar->id)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* arch_get_name */
|
|
char const * arch_get_name(Arch * arch)
|
|
{
|
|
return arch->plugin->name;
|
|
}
|
|
|
|
|
|
/* arch_get_register */
|
|
ArchRegister * arch_get_register(Arch * arch, size_t index)
|
|
{
|
|
if(index >= arch->registers_cnt)
|
|
return NULL;
|
|
return &arch->plugin->registers[index];
|
|
}
|
|
|
|
|
|
/* arch_get_register_by_id_size */
|
|
ArchRegister * arch_get_register_by_id_size(Arch * arch, uint32_t id,
|
|
uint32_t size)
|
|
{
|
|
size_t i;
|
|
|
|
for(i = 0; i < arch->registers_cnt; i++)
|
|
if(arch->plugin->registers[i].id == id
|
|
&& arch->plugin->registers[i].size == size)
|
|
return &arch->plugin->registers[i];
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* arch_get_register_by_name */
|
|
ArchRegister * arch_get_register_by_name(Arch * arch, char const * name)
|
|
{
|
|
size_t i;
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, name);
|
|
#endif
|
|
for(i = 0; i < arch->registers_cnt; i++)
|
|
if(strcmp(arch->plugin->registers[i].name, name) == 0)
|
|
return &arch->plugin->registers[i];
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* arch_get_register_by_name_size */
|
|
ArchRegister * arch_get_register_by_name_size(Arch * arch, char const * name,
|
|
uint32_t size)
|
|
{
|
|
size_t i;
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "DEBUG: %s(\"%s\", %u)\n", __func__, name, size);
|
|
#endif
|
|
for(i = 0; i < arch->registers_cnt; i++)
|
|
if(arch->plugin->registers[i].size != size)
|
|
continue;
|
|
else if(strcmp(arch->plugin->registers[i].name, name) == 0)
|
|
return &arch->plugin->registers[i];
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* useful */
|
|
/* arch_decode */
|
|
static void _decode_print(ArchInstructionCall * call);
|
|
|
|
int arch_decode(Arch * arch)
|
|
{
|
|
ArchInstructionCall call;
|
|
|
|
if(arch->plugin->decode == NULL)
|
|
return -error_set_code(1, "%s: %s", arch->plugin->name,
|
|
"Disassembly not supported");
|
|
while(arch->plugin->decode(arch->plugin, &call) == 0)
|
|
_decode_print(&call);
|
|
return 0;
|
|
}
|
|
|
|
static void _decode_print(ArchInstructionCall * call)
|
|
{
|
|
char const * sep = "\t";
|
|
size_t i;
|
|
ArchOperand * ao;
|
|
char const * name;
|
|
|
|
printf("\t%s", call->name);
|
|
for(i = 0; i < call->operands_cnt; i++)
|
|
{
|
|
ao = &call->operands[i];
|
|
switch(AO_GET_TYPE(call->operands[i].type))
|
|
{
|
|
case AOT_DREGISTER:
|
|
name = ao->value.dregister.name;
|
|
printf("%s[%%%s + $0x%lx]", sep, name,
|
|
ao->value.dregister.offset);
|
|
sep = ", ";
|
|
break;
|
|
case AOT_DREGISTER2:
|
|
name = ao->value.dregister2.name;
|
|
printf("%s[%%%s + %%%s]", sep, name,
|
|
ao->value.dregister2.name2);
|
|
sep = ", ";
|
|
break;
|
|
case AOT_IMMEDIATE:
|
|
printf("%s%s$0x%lx", sep,
|
|
ao->value.immediate.negative
|
|
? "-" : "",
|
|
ao->value.immediate.value);
|
|
sep = ", ";
|
|
break;
|
|
case AOT_REGISTER:
|
|
name = call->operands[i].value._register.name;
|
|
printf("%s%%%s", sep, name);
|
|
sep = ", ";
|
|
break;
|
|
}
|
|
}
|
|
putchar('\n');
|
|
}
|
|
|
|
|
|
/* arch_exit */
|
|
int arch_exit(Arch * arch)
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "DEBUG: %s()\n", __func__);
|
|
#endif
|
|
arch->filename = NULL;
|
|
arch->fp = NULL;
|
|
arch->buffer = NULL;
|
|
arch->buffer_cnt = 0;
|
|
arch->buffer_pos = 0;
|
|
memset(&arch->helper, 0, sizeof(arch->helper));
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* arch_init */
|
|
int arch_init(Arch * arch, char const * filename, FILE * fp)
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "DEBUG: %s(\"%s\", %p)\n", __func__, filename,
|
|
(void *)fp);
|
|
#endif
|
|
arch->filename = filename;
|
|
arch->fp = fp;
|
|
arch->helper.arch = arch;
|
|
arch->helper.get_filename = _arch_get_filename;
|
|
arch->helper.get_instruction_by_opcode = arch_get_instruction_by_opcode;
|
|
arch->helper.get_register_by_id_size = arch_get_register_by_id_size;
|
|
arch->helper.get_register_by_name_size = arch_get_register_by_name_size;
|
|
arch->helper.read = NULL;
|
|
arch->helper.write = _arch_write;
|
|
arch->plugin->helper = &arch->helper;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* arch_init */
|
|
int arch_init_buffer(Arch * arch, char const * buffer, size_t size)
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "DEBUG: %s()\n", __func__);
|
|
#endif
|
|
arch->filename = "buffer";
|
|
arch->buffer = buffer;
|
|
arch->buffer_cnt = size;
|
|
arch->buffer_pos = 0;
|
|
arch->helper.arch = arch;
|
|
arch->helper.get_filename = _arch_get_filename;
|
|
arch->helper.get_instruction_by_opcode = arch_get_instruction_by_opcode;
|
|
arch->helper.get_register_by_id_size = arch_get_register_by_id_size;
|
|
arch->helper.get_register_by_name_size = arch_get_register_by_name_size;
|
|
arch->helper.write = NULL;
|
|
arch->helper.read = _arch_read_buffer;
|
|
arch->plugin->helper = &arch->helper;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* arch_write */
|
|
int arch_write(Arch * arch, ArchInstruction * instruction,
|
|
ArchInstructionCall * call)
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, instruction->name);
|
|
#endif
|
|
return arch->plugin->write(arch->plugin, instruction, call);
|
|
}
|
|
|
|
|
|
/* private */
|
|
/* callbacks */
|
|
/* arch_get_filename */
|
|
static char const * _arch_get_filename(Arch * arch)
|
|
{
|
|
return arch->filename;
|
|
}
|
|
|
|
|
|
/* arch_read_buffer */
|
|
static ssize_t _arch_read_buffer(Arch * arch, void * buf, size_t size)
|
|
{
|
|
ssize_t s = min(arch->buffer_cnt - arch->buffer_pos, size);
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "DEBUG: %s()\n", __func__);
|
|
#endif
|
|
if(s == 0)
|
|
return -error_set_code(1, "%s", "End of buffer reached");
|
|
memcpy(buf, &arch->buffer[arch->buffer_pos], s);
|
|
arch->buffer_pos += s;
|
|
return s;
|
|
}
|
|
|
|
|
|
/* arch_write */
|
|
static ssize_t _arch_write(Arch * arch, void const * buf, size_t size)
|
|
{
|
|
if(fwrite(buf, size, 1, arch->fp) == 1)
|
|
return size;
|
|
if(ferror(arch->fp))
|
|
return -error_set_code(1, "%s: %s", arch->filename,
|
|
strerror(errno));
|
|
if(feof(arch->fp))
|
|
return -error_set_code(1, "%s: %s", arch->filename,
|
|
"End of file reached");
|
|
return -error_set_code(1, "%s: %s", arch->filename, "Write error");
|
|
}
|