/* Chrysalide - Outil d'analyse de fichiers binaires
* instruction.c - gestion générique des instructions
*
* Copyright (C) 2008-2014 Cyrille Bagard
*
* This file is part of Chrysalide.
*
* OpenIDA 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; either version 3 of the License, or
* (at your option) any later version.
*
* OpenIDA 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 Foobar. If not, see .
*/
#include "instruction.h"
#include
#include
#include
#include "instruction-int.h"
#include "../common/extstr.h"
/* Initialise la classe générique des instructions. */
static void g_arch_instruction_class_init(GArchInstructionClass *);
/* Initialise une instance d'opérande d'architecture. */
static void g_arch_instruction_init(GArchInstruction *);
/* Supprime toutes les références externes. */
static void g_arch_instruction_dispose(GArchInstruction *);
/* Procède à la libération totale de la mémoire. */
static void g_arch_instruction_finalize(GArchInstruction *);
/* --------------------- CONVERSIONS DU FORMAT DES INSTRUCTIONS --------------------- */
/* Ajoute à un tampon GLib le contenu de l'instance spécifiée. */
static GBufferLine *_g_arch_instruction_print(const GArchInstruction *, GCodeBuffer *, MemoryDataSize, const bin_t *, AsmSyntax);
/* Indique le type défini pour une instruction d'architecture. */
G_DEFINE_TYPE(GArchInstruction, g_arch_instruction, G_TYPE_OBJECT);
/******************************************************************************
* *
* Paramètres : klass = classe à initialiser. *
* *
* Description : Initialise la classe générique des instructions. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_arch_instruction_class_init(GArchInstructionClass *klass)
{
GObjectClass *object; /* Autre version de la classe */
GArchInstructionClass *instr; /* Encore une autre vision... */
object = G_OBJECT_CLASS(klass);
object->dispose = (GObjectFinalizeFunc/* ! */)g_arch_instruction_dispose;
object->finalize = (GObjectFinalizeFunc)g_arch_instruction_finalize;
instr = G_ARCH_INSTRUCTION_CLASS(klass);
instr->print = (print_instruction_fc)_g_arch_instruction_print;
}
/******************************************************************************
* *
* Paramètres : instr = instance à initialiser. *
* *
* Description : Initialise une instance d'instruction d'architecture. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_arch_instruction_init(GArchInstruction *instr)
{
DL_LIST_ITEM_INIT(&instr->flow);
}
/******************************************************************************
* *
* Paramètres : instr = instance d'objet GLib à traiter. *
* *
* Description : Supprime toutes les références externes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_arch_instruction_dispose(GArchInstruction *instr)
{
G_OBJECT_CLASS(g_arch_instruction_parent_class)->dispose(G_OBJECT(instr));
}
/******************************************************************************
* *
* Paramètres : instr = instance d'objet GLib à traiter. *
* *
* Description : Procède à la libération totale de la mémoire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_arch_instruction_finalize(GArchInstruction *instr)
{
G_OBJECT_CLASS(g_arch_instruction_parent_class)->finalize(G_OBJECT(instr));
}
/******************************************************************************
* *
* Paramètres : instr = instruction quelconque à modifier. *
* suffix = chaîne de caractères fournie en complément. *
* *
* Description : Etend la désignation d'un nom d'instruction. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_arch_instruction_append_suffix(GArchInstruction *instr, const char *suffix)
{
instr->suffix = suffix;
}
/******************************************************************************
* *
* Paramètres : instr = instruction quelconque à modifier. *
* type = type de procédure à mémoriser. *
* hook = fonction à appeler sur commande. *
* *
* Description : Définit un traitement complémentare au désassemblage. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_arch_instruction_set_hook(GArchInstruction *instr, InstrProcessHook type, instr_hook_fc hook)
{
assert(type < IPH_COUNT);
instr->hooks[type] = hook;
}
/******************************************************************************
* *
* Paramètres : instr = instruction quelconque à traiter. *
* type = type de procédure à utiliser. *
* context = contexte associé à la phase de désassemblage. *
* format = accès aux données du binaire d'origine. *
* *
* Description : Complète un désassemblage accompli pour une instruction. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_arch_instruction_call_hook(GArchInstruction *instr, InstrProcessHook type, GProcContext *context, GBinFormat *format)
{
assert(type < IPH_COUNT);
if (instr->hooks[type] != NULL)
instr->hooks[type](instr, context, format);
}
/******************************************************************************
* *
* Paramètres : instr = instruction quelconque à modifier. *
* address = adresse virtuelle et/ou position physique. *
* length = taille de l'instruction. *
* *
* Description : Définit la localisation d'une instruction. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_arch_instruction_set_range(GArchInstruction *instr, const mrange_t *range)
{
copy_mrange(&instr->range, range);
}
/******************************************************************************
* *
* Paramètres : instr = instruction quelconque à consulter. *
* *
* Description : Fournit la place mémoire d'une instruction. *
* *
* Retour : Zone mémoire couverte par l'instruction. *
* *
* Remarques : - *
* *
******************************************************************************/
const mrange_t *g_arch_instruction_get_range(const GArchInstruction *instr)
{
return &instr->range;
}
/******************************************************************************
* *
* Paramètres : instr = instruction quelconque à consulter. *
* offset = position physique dans le code binaire/NULL. [OUT] *
* length = taille de l'instruction ou NULL. [OUT] *
* address = adresse virtuelle ou position physique/NULL. [OUT] *
* *
* Description : Fournit la localisation d'une instruction. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_arch_instruction_get_location(const GArchInstruction *instr, off_t *offset, off_t *length, vmpa_t *address)
{
if (offset != NULL) *offset = instr->offset;
if (length != NULL) *length = instr->length;
if (address != NULL) *address = instr->address;
}
/******************************************************************************
* *
* Paramètres : instr = instance à mettre à jour. *
* opererand = instruction à venir associer. *
* *
* Description : Attache un opérande supplémentaire à une instruction. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_arch_instruction_attach_extra_operand(GArchInstruction *instr, GArchOperand *operand)
{
instr->operands_count++;
instr->operands = (GArchOperand **)realloc(instr->operands, instr->operands_count * sizeof(GArchOperand *));
instr->operands[instr->operands_count - 1] = operand;
}
/******************************************************************************
* *
* Paramètres : instr = instance à consulter. *
* *
* Description : Indique la quantité d'opérandes présents dans l'instruction. *
* *
* Retour : Nombre d'opérandes attachés. *
* *
* Remarques : - *
* *
******************************************************************************/
size_t g_arch_instruction_count_operands(const GArchInstruction *instr)
{
return instr->operands_count;
}
/******************************************************************************
* *
* Paramètres : instr = instance à mettre à jour. *
* index = indice de l'opérande concernée. *
* *
* Description : Fournit un opérande donné d'une instruction. *
* *
* Retour : Opérande trouvée ou NULL si aucune. *
* *
* Remarques : - *
* *
******************************************************************************/
GArchOperand *g_arch_instruction_get_operand(const GArchInstruction *instr, size_t index)
{
GArchOperand *result; /* Opérande à retourner */
if (index >= instr->operands_count) result = NULL;
else result = instr->operands[index];
/* TODO : incrémenter la référence ! */
return result;
}
/******************************************************************************
* *
* Paramètres : instr = instance à mettre à jour. *
* new = nouvelle opérande à attacher. *
* old = ancienne opérande à détacher. *
* *
* Description : Remplace un opérande d'une instruction par un autre. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_arch_instruction_replace_operand(GArchInstruction *instr, GArchOperand *new, const GArchOperand *old)
{
size_t i; /* Boucle de parcours */
for (i = 0; i < instr->operands_count; i++)
if (instr->operands[i] == old)
break;
if (i < instr->operands_count)
{
g_object_unref(G_OBJECT(instr->operands[i]));
instr->operands[i] = new;
}
}
/******************************************************************************
* *
* Paramètres : instr = instance à mettre à jour. *
* opererand = instruction à venir dissocier. *
* *
* Description : Détache un opérande liée d'une instruction. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_arch_instruction_detach_operand(GArchInstruction *instr, GArchOperand *operand)
{
size_t i; /* Boucle de parcours */
for (i = 0; i < instr->operands_count; i++)
if (instr->operands[i] == operand)
break;
if ((i + 1) < instr->operands_count)
memmove(&instr->operands[i], &instr->operands[i + 1],
(instr->operands_count - i - 1) * sizeof(GArchOperand *));
instr->operands_count--;
instr->operands = (GArchOperand **)realloc(instr->operands, instr->operands_count * sizeof(GArchOperand *));
}
/******************************************************************************
* *
* Paramètres : instr = instruction à consulter. *
* rregs = liste des rgistres lus. [OUT] *
* rcount = nombre de registres lus. [OUT] *
* wregs = liste des rgistres écrits. [OUT] *
* wcount = nombre de registres écrits. [OUT] *
* *
* Description : Liste les registres lus et écrits par l'instruction. *
* *
* Retour : - *
* *
* Remarques : Les compteurs de références sont à décrémenter après usage ! *
* *
******************************************************************************/
void g_arch_instruction_get_rw_registers(const GArchInstruction *instr, GArchRegister ***rregs, size_t *rcount, GArchRegister ***wregs, size_t *wcount)
{
size_t i; /* Boucle de parcours */
*rregs = NULL;
*rcount = 0;
*wregs = NULL;
*wcount = 0;
instr->get_rw_regs(instr, rregs, rcount, wregs, wcount);
for (i = 0; i < *rcount; i++)
g_object_ref(G_OBJECT((*rregs)[i]));
for (i = 0; i < *wcount; i++)
g_object_ref(G_OBJECT((*wregs)[i]));
}
/* ---------------------------------------------------------------------------------- */
/* DEFINITION DES LIAISONS ENTRE INSTRUCTIONS */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : instr = instruction à consulter. *
* addr = eventuelle adresse associée à faire connaître. [OUT] *
* *
* Description : Informe sur une éventuelle référence à une autre instruction.*
* *
* Retour : Type de lien trouvé ou ILT_NONE si aucun. *
* *
* Remarques : - *
* *
******************************************************************************/
InstructionLinkType g_arch_instruction_get_link(const GArchInstruction *instr, vmpa_t *addr)
{
return instr->get_link(instr, addr);
}
/******************************************************************************
* *
* Paramètres : instr = instruction à consulter. *
* *
* Description : Indique si l'instruction correspond à un retour de fonction. *
* *
* Retour : true si l'instruction est un 'return' quelconque ou false. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_arch_instruction_is_return(const GArchInstruction *instr)
{
return instr->is_return(instr);
}
/******************************************************************************
* *
* Paramètres : instr = instruction dont les informations sont à consulter. *
* dest = ligne visée par la liaison (côté destination). *
* type = type de lien à construire. *
* ... = éventuelles informations complémentaires. *
* *
* Description : Etablit un lien entre deux instructions. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_arch_instruction_link_with(GArchInstruction *instr, GArchInstruction *dest, InstructionLinkType type, ...)
{
size_t count; /* Raccourci pour la lecture */
va_list ap; /* Gestion des variations */
/* Côté destination */
count = ++dest->from_count;
dest->from = (GArchInstruction **)realloc(dest->from,
count * sizeof(GArchInstruction *));
dest->from_types = (InstructionLinkType *)realloc(dest->from_types,
count * sizeof(InstructionLinkType));
dest->from[count - 1] = instr;
dest->from_types[count - 1] = type;
/* Côté point de départ */
count = ++instr->to_count;
instr->to = (GArchInstruction **)realloc(instr->to,
count * sizeof(GArchInstruction *));
instr->to_types = (InstructionLinkType *)realloc(instr->to_types,
count * sizeof(InstructionLinkType));
instr->links_info = (link_extra_info *)realloc(instr->links_info,
count * sizeof(link_extra_info));
instr->to[count - 1] = dest;
instr->to_types[count - 1] = type;
va_start(ap, type);
switch (type)
{
case ILT_CASE_JUMP:
instr->links_info[count - 1].imm = va_arg(ap, GImmOperand *);
break;
default:
break;
}
va_end(ap);
}
/******************************************************************************
* *
* Paramètres : instr = instruction dont les informations sont à consulter. *
* *
* Description : Indique si l'instruction a une ou plusieurs origines. *
* *
* Retour : Bilan de la consultation. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_arch_instruction_has_sources(const GArchInstruction *instr)
{
return (instr->from_count > 0);
}
/******************************************************************************
* *
* Paramètres : instr = instruction dont les informations sont à consulter. *
* dests = liste des instructions de destination. [OUT] *
* types = liste des types de liens présents. [OUT] *
* *
* Description : Fournit les origines d'une instruction donnée. *
* *
* Retour : Nombre de ces destinations. *
* *
* Remarques : - *
* *
******************************************************************************/
size_t g_arch_instruction_get_sources(const GArchInstruction *instr, GArchInstruction ***dests, InstructionLinkType **types)
{
*dests = instr->from;
if (types != NULL)
*types = instr->from_types;
return instr->from_count;
}
/******************************************************************************
* *
* Paramètres : instr = instruction dont les informations sont à consulter. *
* *
* Description : Indique si l'instruction a une suite autre que la suivante. *
* *
* Retour : Bilan de la consultation. *
* *
* Remarques : - *
* *
******************************************************************************/
bool g_arch_instruction_has_destinations(const GArchInstruction *instr)
{
/* FIXME !? */
//return (instr->to_count > 1 || (instr->to_count == 1 && instr->to_types[0] != ILT_CALL));
return (instr->to_count > 0);
}
/******************************************************************************
* *
* Paramètres : instr = instruction dont les informations sont à consulter. *
* dests = liste des instructions de destination. [OUT] *
* types = liste des types de liens présents. [OUT] *
* info = éventuelles informations complémentaires. [OUT] *
* *
* Description : Fournit les destinations d'une instruction donnée. *
* *
* Retour : Nombre de ces destinations. *
* *
* Remarques : - *
* *
******************************************************************************/
size_t g_arch_instruction_get_destinations(const GArchInstruction *instr, GArchInstruction ***dests, InstructionLinkType **types, link_extra_info **info)
{
*dests = instr->to;
if (types != NULL)
*types = instr->to_types;
if (info != NULL)
*info = instr->links_info;
return instr->to_count;
}
/******************************************************************************
* *
* Paramètres : instr = instruction dont les informations sont à consulter. *
* type = type de lien recherché. *
* *
* Description : Fournit la destination d'une instruction et d'un type donné. *
* *
* Retour : Instruction de destination trouvée ou NULL. *
* *
* Remarques : - *
* *
******************************************************************************/
GArchInstruction *g_arch_instruction_get_given_destination(const GArchInstruction *instr, InstructionLinkType type)
{
GArchInstruction *result; /* Résultat à remonter */
size_t i; /* Boucle de parcours */
result = NULL;
for (i = 0; i < instr->to_count && result == NULL; i++)
if (instr->to_types[i] == type)
result = instr->to[i];
return result;
}
/******************************************************************************
* *
* Paramètres : iter = membre du groupe donné en référence. *
* list = liste des instructions à analyser. *
* count = taille de cette liste. *
* *
* Description : Indique la position dans les instructions identiques. *
* *
* Retour : Indice dans les instructions identiques du groupe. *
* *
* Remarques : - *
* *
******************************************************************************/
size_t g_arch_instruction_compute_group_index(GArchInstruction **iter, GArchInstruction **list, size_t count)
{
size_t result; /* Valeur à retourner */
size_t i; /* Boucle de parcours */
result = 0;
for (i = 0; i < count; i++)
{
if ((list + i) == iter)
break;
if (list[i] == *iter)
result++;
}
return result;
}
/* ---------------------------------------------------------------------------------- */
/* CONVERSIONS DU FORMAT DES INSTRUCTIONS */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : instr = instruction d'assemblage à consulter. *
* syntax = type de représentation demandée. *
* *
* Description : Fournit le nom humain de l'instruction manipulée. *
* *
* Retour : Mot clef de bas niveau. *
* *
* Remarques : - *
* *
******************************************************************************/
const char *g_arch_instruction_get_keyword(const GArchInstruction *instr, AsmSyntax syntax)
{
if (instr->cached_keyword == NULL)
G_ARCH_INSTRUCTION_GET_CLASS(instr)->build_key(instr, syntax);
return instr->cached_keyword;
}
/******************************************************************************
* *
* Paramètres : instr = instruction d'assemblage à représenter. *
* buffer = espace où placer ledit contenu. *
* msize = taille idéale des positions et adresses; *
* syntax = type de représentation demandée. *
* *
* Description : Ajoute à un tampon GLib le contenu de l'instance spécifiée. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static GBufferLine *_g_arch_instruction_print(const GArchInstruction *instr, GCodeBuffer *buffer, MemoryDataSize msize, const bin_t *content, AsmSyntax syntax)
{
GBufferLine *result; /* Ligne de destination */
const char *key; /* Mot clef principal */
size_t klen; /* Taille de ce mot clef */
size_t i; /* Boucle de parcours */
result = g_code_buffer_append_new_line(buffer, &instr->range);
g_buffer_line_fill_for_instr(result, msize/* TODO ! */, msize,
content, get_mrange_length(&instr->range), true);
/* Instruction proprement dite */
key = g_arch_instruction_get_keyword(instr, syntax);
klen = strlen(key);
g_buffer_line_insert_text(result, BLC_ASSEMBLY_HEAD, key, klen, RTT_INSTRUCTION);
if (instr->operands_count > 0)
{
g_arch_operand_print(instr->operands[0], result, syntax);
for (i = 1; i < instr->operands_count; i++)
{
g_buffer_line_insert_text(result, BLC_ASSEMBLY, ",", 1, RTT_PUNCT);
g_buffer_line_insert_text(result, BLC_ASSEMBLY, " ", 1, RTT_RAW);
g_arch_operand_print(instr->operands[i], result, syntax);
}
}
return result;
}
/******************************************************************************
* *
* Paramètres : instr = instruction d'assemblage à représenter. *
* buffer = espace où placer ledit contenu. *
* msize = taille idéale des positions et adresses; *
* syntax = type de représentation demandée. *
* *
* Description : Ajoute à un tampon GLib le contenu de l'instance spécifiée. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
GBufferLine *g_arch_instruction_print(const GArchInstruction *instr, GCodeBuffer *buffer, MemoryDataSize msize, const bin_t *content, AsmSyntax syntax)
{
return G_ARCH_INSTRUCTION_GET_CLASS(instr)->print(instr, buffer, msize, content, syntax);
}
/******************************************************************************
* *
* Paramètres : instr = instruction d'origine à convertir. *
* ctx = contexte de la phase de décompilation. *
* *
* Description : Décompile une instruction de façon générique. *
* *
* Retour : Instruction mise en place ou NULL. *
* *
* Remarques : - *
* *
******************************************************************************/
GDecInstruction *g_arch_instruction_decompile(const GArchInstruction *instr, GDecContext *ctx)
{
GDecInstruction *result; /* Instruction à retourner */
GDecInstruction *list; /* Instructions décompilées */
if (instr->decomp != NULL)
{
result = instr->decomp(instr, ctx);
if (result != NULL)
{
list = g_dec_context_get_decomp_instrs(ctx);
if (list == NULL) list = result;
else g_dec_instruction_add_to_list(&list, result);
g_dec_context_set_decomp_instrs(ctx, list);
}
}
else
result = NULL;
return result;
}
/* ---------------------------------------------------------------------------------- */
/* TRAITEMENT DES INSTRUCTIONS PAR ENSEMBLE */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : list = liste d'instructions à consulter. *
* *
* Description : Renvoie vers la dernière instruction d'une série. *
* *
* Retour : Dernière instruction trouvée (ou NULL ?!). *
* *
* Remarques : - *
* *
******************************************************************************/
GArchInstruction *g_arch_instruction_find_last(const GArchInstruction *list)
{
return ainstr_list_last(list);
}
/******************************************************************************
* *
* Paramètres : list = liste d'instructions à compléter, ou NULL. *
* instr = nouvelle instruction à intégrer à l'ensemble. *
* *
* Description : Ajoute une instruction à un ensemble existant. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_arch_instruction_add_to_list(GArchInstruction **list, GArchInstruction *instr)
{
ainstr_list_add_tail(instr, list);
}
/******************************************************************************
* *
* Paramètres : list1 = première liste à traiter. *
* list2 = seconde liste à traiter. *
* *
* Description : Fusionne deux listes d'instructions. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_arch_instruction_merge_lists(GArchInstruction **list1, GArchInstruction **list2)
{
ainstr_list_merge(list1, list2);
}
/******************************************************************************
* *
* Paramètres : list = liste d'instructions à consulter. *
* : iter = position actuelle dans la liste. *
* *
* Description : Fournit l'élement suivant un autre pour un parcours. *
* *
* Retour : Elément suivant ou NULL si aucun. *
* *
* Remarques : - *
* *
******************************************************************************/
GArchInstruction *g_arch_instruction_get_prev_iter(const GArchInstruction *list, const GArchInstruction *iter)
{
GArchInstruction *result; /* Elément suivant à renvoyer */
result = ainstr_list_prev_iter(iter, list);
return result;
}
/******************************************************************************
* *
* Paramètres : list = liste d'instructions à consulter. *
* : iter = position actuelle dans la liste. *
* max = adresse marquant la limite (exclue) du parcours. *
* *
* Description : Fournit l'élement suivant un autre pour un parcours. *
* *
* Retour : Elément suivant ou NULL si aucun. *
* *
* Remarques : - *
* *
******************************************************************************/
GArchInstruction *g_arch_instruction_get_next_iter(const GArchInstruction *list, const GArchInstruction *iter, vmpa_t max)
{
GArchInstruction *result; /* Elément suivant à renvoyer */
result = ainstr_list_next_iter(iter, list);
/* FIXME : utiliser les nouvelles adresses !
if (result != NULL && result->address >= max)
result = NULL;
*/
return result;
}
/******************************************************************************
* *
* Paramètres : list = liste de lignes à parcourir. *
* addr = position en mémoire ou physique à chercher. *
* strict = définit la considération à porter à l'adresse. *
* *
* Description : Recherche une instruction d'après son adresse. *
* *
* Retour : Instruction trouvée à l'adresse donnée, NULL si aucune. *
* *
* Remarques : - *
* *
******************************************************************************/
GArchInstruction *g_arch_instruction_find_by_address(GArchInstruction *list, vmpa_t addr, bool strict)
{
GArchInstruction *result; /* Trouvaille à retourner */
ainstr_list_for_each(result, list)
{
if (strict && result->offset == addr) break;
else if (!strict && result->offset < addr
&& addr < (result->offset + result->length)) break;
}
return result;
}