/* OpenIDA - Outil d'analyse de fichiers binaires
* instruction.c - gestion générique des instructions
*
* Copyright (C) 2008 Cyrille Bagard
*
* This file is part of OpenIDA.
*
* 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 "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 *);
/* Ajoute du texte simple à un fichier ouvert en écriture. */
static void g_arch_instruction_add_text(const GArchInstruction *, GRenderingOptions *, MainRendering, FILE *) __attribute__ ((deprecated));
/* Ajoute à un tampon GLib le contenu de l'instance spécifiée. */
static void g_arch_instruction_to_buffer(const GArchInstruction *, GBufferLine *, GRenderingOptions *) __attribute__ ((deprecated));
/* --------------------- CONVERSIONS DU FORMAT DES INSTRUCTIONS --------------------- */
/* Ajoute à un tampon GLib le contenu de l'instance spécifiée. */
static void _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_CONTENT_EXPORTER);
/******************************************************************************
* *
* 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)
{
}
/******************************************************************************
* *
* Paramètres : instr = instance à initialiser. *
* *
* Description : Initialise une instance d'instruction d'architecture. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_arch_instruction_init(GArchInstruction *instr)
{
GContentExporter *parent; /* Instance parente */
parent = G_CONTENT_EXPORTER(instr);
parent->add_text = (add_text_fc)g_arch_instruction_add_text;
parent->export_buffer = (export_buffer_fc)g_arch_instruction_to_buffer;
DL_LIST_ITEM_INIT(&instr->flow);
}
/******************************************************************************
* *
* Paramètres : instr = instruction à transcrire. *
* options = options de rendu. *
* rendering = support effectif final des lignes de code. *
* stream = flux ouvert en écriture. *
* *
* Description : Ajoute du texte simple à un fichier ouvert en écriture. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_arch_instruction_add_text(const GArchInstruction *instr, GRenderingOptions *options, MainRendering rendering, FILE *stream)
{
GContentExporter *exporter; /* Autre vision de l'objet */
const char *key; /* Mot clef principal */
size_t klen; /* Taille de ce mot clef */
size_t i; /* Boucle de parcours */
exporter = G_CONTENT_EXPORTER(instr);
key = instr->get_text(instr,
g_rendering_options_get_format(options),
g_rendering_options_get_syntax(options));
klen = strlen(key);
g_content_exporter_insert_text(exporter, stream, key, klen, RTT_INSTRUCTION);
if (instr->operands_count > 0)
{
g_content_exporter_insert_text(exporter, stream, "\t", 1, RTT_NONE);
g_content_exporter_add_text(G_CONTENT_EXPORTER(G_ARCH_INSTRUCTION(instr)->operands[0]),
options, rendering, stream);
for (i = 1; i < instr->operands_count; i++)
{
g_content_exporter_insert_text(exporter, stream, ",", 1, RTT_NONE/* FIXME */);
g_content_exporter_insert_text(exporter, stream, " ", 1, RTT_NONE);
g_content_exporter_add_text(G_CONTENT_EXPORTER(G_ARCH_INSTRUCTION(instr)->operands[i]),
options, rendering, stream);
}
}
}
/******************************************************************************
* *
* Paramètres : instr = instruction d'assemblage à représenter. *
* buffer = espace où placer ledit contenu. *
* options = options de rendu. *
* *
* Description : Ajoute à un tampon GLib le contenu de l'instance spécifiée. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_arch_instruction_to_buffer(const GArchInstruction *instr, GBufferLine *buffer, GRenderingOptions *options)
{
GContentExporter *exporter; /* Autre vision de l'objet */
const char *key; /* Mot clef principal */
size_t klen; /* Taille de ce mot clef */
size_t i; /* Boucle de parcours */
exporter = G_CONTENT_EXPORTER(instr);
key = instr->get_text(instr,
g_rendering_options_get_format(options),
g_rendering_options_get_syntax(options));
klen = strlen(key);
g_content_exporter_insert_into_buffer(exporter, buffer, BLC_ASSEMBLY_HEAD,
key, klen, RTT_INSTRUCTION);
if (instr->operands_count > 0)
{
g_content_exporter_to_buffer(G_CONTENT_EXPORTER(G_ARCH_INSTRUCTION(instr)->operands[0]),
buffer, options);
for (i = 1; i < instr->operands_count; i++)
{
g_content_exporter_insert_into_buffer(exporter, buffer, BLC_ASSEMBLY,
",", 1, RTT_NONE/* FIXME */);
g_content_exporter_insert_into_buffer(exporter, buffer, BLC_ASSEMBLY,
" ", 1, RTT_NONE);
g_content_exporter_to_buffer(G_CONTENT_EXPORTER(G_ARCH_INSTRUCTION(instr)->operands[i]),
buffer, options);
}
}
}
/******************************************************************************
* *
* Paramètres : instr = instruction quelconque à modifier. *
* offset = position physique dans le code binaire. *
* length = taille de l'instruction. *
* address = adresse virtuelle ou position physique. *
* *
* Description : Définit la localisation d'une instruction. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_arch_instruction_set_location(GArchInstruction *instr, off_t offset, off_t length, vmpa_t address)
{
instr->offset = offset;
instr->length = length;
instr->address = address;
}
/******************************************************************************
* *
* 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];
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)
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 à traiter. *
* format = format du binaire manipulé. *
* syntax = type de représentation demandée. *
* *
* Description : Traduit une instruction en version humainement lisible. *
* *
* Retour : Chaîne de caractères à libérer de la mémoire. *
* *
* Remarques : - *
* *
******************************************************************************/
char *g_arch_instruction_get_text(const GArchInstruction *instr, const GExeFormat *format, AsmSyntax syntax)
{
char *result; /* Chaîne à retourner */
size_t i; /* Boucle de parcours */
char *opstr; /* Chaîne d'opérande */
return strdup("");
if (instr->operands_count == 0)
result = strdup(instr->get_text(instr, format, syntax));
else
{
result = g_arch_operand_get_text(G_ARCH_INSTRUCTION(instr)->operands[0], format, syntax);
for (i = 1; i < instr->operands_count; i++)
{
result = stradd(result, ", ");
opstr = g_arch_operand_get_text(G_ARCH_INSTRUCTION(instr)->operands[i], format, syntax);
result = stradd(result, opstr);
free(opstr);
}
result = strprep(result, "\t");
result = strprep(result, instr->get_text(instr, format, syntax));
}
return result;
}
/* ---------------------------------------------------------------------------------- */
/* 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. *
* *
* Description : Etablit un lien entre deux instructions. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_arch_instruction_link_with(GArchInstruction *instr, GArchInstruction *dest, InstructionLinkType type)
{
/* Côté destination */
dest->from = (GArchInstruction **)realloc(dest->from,
++dest->from_count * sizeof(GArchInstruction *));
dest->from[dest->from_count - 1] = instr;
/* Côté point de départ */
instr->to_count++;
instr->to = (GArchInstruction **)realloc(instr->to,
instr->to_count * sizeof(GArchInstruction *));
instr->links_type = (InstructionLinkType *)realloc(instr->links_type,
instr->to_count * sizeof(InstructionLinkType));
instr->to[instr->to_count - 1] = dest;
instr->links_type[instr->to_count - 1] = type;
}
/******************************************************************************
* *
* 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. *
* *
* 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)
{
return (instr->to_count > 1 || (instr->to_count == 1 && instr->links_type[0] != ILT_CALL));
}
/******************************************************************************
* *
* 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 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)
{
*dests = instr->to;
*types = instr->links_type;
return instr->to_count;
}
/* ---------------------------------------------------------------------------------- */
/* CONVERSIONS DU FORMAT DES INSTRUCTIONS */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : instr = instruction d'assemblage à représenter. *
* buffer = espace où placer ledit contenu. *
* syntax = type de représentation demandée. *
* *
* Description : Ajoute à un tampon GLib le contenu de l'instance spécifiée. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void _g_arch_instruction_print(const GArchInstruction *instr, GCodeBuffer *buffer, MemoryDataSize msize, const bin_t *content, AsmSyntax syntax)
{
GBufferLine *line; /* Ligne de destination */
char address[VMPA_MAX_SIZE]; /* Adresse au format texte */
size_t len; /* Taille de l'élément inséré */
char *bin_code; /* Tampon du code binaire */
off_t i; /* Boucle de parcours #1 */
const char *key; /* Mot clef principal */
size_t klen; /* Taille de ce mot clef */
size_t j; /* Boucle de parcours #2 */
line = g_code_buffer_append_new_line(buffer);
/* Adresse virtuelle ou physique */
len = vmpa_to_string(instr->address, msize, address);
g_buffer_line_insert_text(line, BLC_ADDRESS, address, len, RTT_RAW);
/* Code brut */
bin_code = (char *)calloc(instr->length * 3, sizeof(char));
for (i = 0; i < instr->length; i++)
{
if ((i + 1) < instr->length)
snprintf(&bin_code[i * (2 + 1)], 4, "%02hhx ", content[instr->offset + i]);
else
snprintf(&bin_code[i * (2 + 1)], 3, "%02hhx", content[instr->offset + i]);
}
g_buffer_line_insert_text(line, BLC_BINARY,
bin_code, instr->length * 3 - 1, RTT_RAW_CODE);
free(bin_code);
/* Instruction proprement dite */
key = instr->get_text(instr, NULL/* FIXME */, 0/* FIXME */);
klen = strlen(key);
g_buffer_line_insert_text(line, BLC_ASSEMBLY_HEAD, key, klen, RTT_INSTRUCTION);
if (instr->operands_count > 0)
{
g_arch_operand_print(instr->operands[0], line, syntax);
for (j = 1; j < instr->operands_count; j++)
{
g_buffer_line_insert_text(line, BLC_ASSEMBLY, ",", 1, RTT_PUNCT);
g_buffer_line_insert_text(line, BLC_ASSEMBLY, " ", 1, RTT_NONE);
g_arch_operand_print(instr->operands[j], line, syntax);
}
}
}
/******************************************************************************
* *
* Paramètres : instr = instruction d'assemblage à représenter. *
* buffer = espace où placer ledit contenu. *
* syntax = type de représentation demandée. *
* *
* Description : Ajoute à un tampon GLib le contenu de l'instance spécifiée. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_arch_instruction_print(const GArchInstruction *instr, GCodeBuffer *buffer, MemoryDataSize msize, const bin_t *content, AsmSyntax syntax)
{
if (instr->print != NULL)
instr->print(instr, buffer, msize, content, syntax);
else
_g_arch_instruction_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 */
if (instr->decomp != NULL)
result = instr->decomp(instr, ctx);
else
result = NULL;
return result;
}
/* ---------------------------------------------------------------------------------- */
/* TRAITEMENT DES INSTRUCTIONS PAR ENSEMBLE */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* 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 : 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);
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;
}