/* Chrysalide - Outil d'analyse de fichiers binaires
* encoding.c - représentation complète d'un encodage
*
* Copyright (C) 2018 Cyrille Bagard
*
* This file is part of Chrysalide.
*
* Chrysalide 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.
*
* Chrysalide 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 Chrysalide. If not, see .
*/
#include "encoding.h"
#include
#include
#include
#include
#include
#include "helpers.h"
#include "qckcall.h"
/* Mémorisation d'un encodage complet */
struct _encoding_spec
{
char *prefix; /* Distinction principale */
char *lprefix; /* Distinction en minuscules */
unsigned int index; /* Distinction secondaire */
operands_format *format; /* Définition des opérandes */
coding_bits *bits; /* Encodage des bits associés */
instr_hooks *hooks; /* Fonctions complémentaires */
encoding_syntax **syntaxes; /* Définitions déjà en place */
size_t syntax_count; /* Nombre de ces définitions */
};
/******************************************************************************
* *
* Paramètres : - *
* *
* Description : Crée un nouveau suivi de l'encodage d'une instruction. *
* *
* Retour : Nouvelle structure prête à emploi. *
* *
* Remarques : - *
* *
******************************************************************************/
encoding_spec *create_encoding_spec(void)
{
encoding_spec *result; /* Définition vierge à renvoyer*/
result = (encoding_spec *)calloc(1, sizeof(encoding_spec));
result->format = create_operands_format();
result->bits = create_coding_bits();
result->hooks = create_instr_hooks();
return result;
}
/******************************************************************************
* *
* Paramètres : spec = spécification d'encodage à libérer de la mémoire. *
* *
* Description : Supprime de la mémoire un suivi d'encodage d'une instruction.*
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void delete_encoding_spec(encoding_spec *spec)
{
size_t i; /* Boucle de parcours */
delete_operands_format(spec->format);
delete_coding_bits(spec->bits);
delete_instr_hooks(spec->hooks);
if (spec->syntaxes != NULL)
{
for (i = 0; i < spec->syntax_count; i++)
delete_encoding_syntax(spec->syntaxes[i]);
free(spec->syntaxes);
}
free(spec);
}
/******************************************************************************
* *
* Paramètres : spec = spécification d'encodage à compléter. *
* prefix = distinction principale entre les définitions. *
* index = distinction secondaire entre les définitions. *
* *
* Description : Définit le nom de code d'une spécification d'encodage. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void define_encoding_spec_code_name(encoding_spec *spec, char *prefix, unsigned int index)
{
spec->prefix = prefix;
spec->lprefix = make_string_lower(strdup(prefix));
spec->index = index;
}
/******************************************************************************
* *
* Paramètres : spec = spécification d'encodage à consulter. *
* prefix = distinction principale entre les définitions. *
* *
* Description : Indique si une spécification se range dans une catégorie. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
bool has_encoding_spec_prefix(const encoding_spec *spec, const char *prefix)
{
bool result; /* Bilan à renvoyer */
if (spec->prefix == NULL && prefix == NULL)
result = true;
else if (spec->prefix != NULL && prefix != NULL)
result = strcmp(spec->prefix, prefix) == 0;
else
result = false;
return result;
}
/******************************************************************************
* *
* Paramètres : spec = spécification d'encodage à consulter. *
* *
* Description : Construit la distinction propre à un encodage. *
* *
* Retour : Distinction à libérer de la mémoire après usage. *
* *
* Remarques : - *
* *
******************************************************************************/
char *build_encoding_spec_prefix(const encoding_spec *spec)
{
char *result; /* Chaîne à retourner */
int ret; /* Recette de construction */
assert(spec->lprefix);
ret = asprintf(&result, "%s%u", spec->lprefix, spec->index);
if (ret == -1)
result = NULL;
return result;
}
/******************************************************************************
* *
* Paramètres : spec = spécification d'encodage à consulter. *
* *
* Description : Fournit le gestionnaire des définitions d'opérandes. *
* *
* Retour : Structure assurant la définition des opérandes. *
* *
* Remarques : - *
* *
******************************************************************************/
operands_format *get_format_in_encoding_spec(const encoding_spec *spec)
{
return spec->format;
}
/******************************************************************************
* *
* Paramètres : spec = spécification d'encodage à consulter. *
* *
* Description : Fournit le gestionnaire des bits d'un encodage d'instruction.*
* *
* Retour : Structure assurant le suivi des bits. *
* *
* Remarques : - *
* *
******************************************************************************/
coding_bits *get_bits_in_encoding_spec(const encoding_spec *spec)
{
return spec->bits;
}
/******************************************************************************
* *
* Paramètres : spec = spécification d'encodage à consulter. *
* *
* Description : Fournit la liste des fonctions à lier à une instruction. *
* *
* Retour : Structure assurant la gestion des fonctions de conversion. *
* *
* Remarques : - *
* *
******************************************************************************/
instr_hooks *get_hooks_in_encoding_spec(const encoding_spec *spec)
{
return spec->hooks;
}
/******************************************************************************
* *
* Paramètres : spec = spécification d'encodage à étendre. *
* *
* Description : Enregistre une définition de syntaxe supplémentaire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void push_new_encoding_syntax(encoding_spec *spec)
{
encoding_syntax *syntax; /* Définition à compléter */
syntax = create_encoding_syntax();
spec->syntaxes = realloc(spec->syntaxes, ++spec->syntax_count * sizeof(encoding_syntax *));
spec->syntaxes[spec->syntax_count - 1] = syntax;
}
/******************************************************************************
* *
* Paramètres : spec = spécification d'encodage à consulter. *
* *
* Description : Fournit un lien vers la définition de syntaxe courante. *
* *
* Retour : Définition en cours d'édition. *
* *
* Remarques : - *
* *
******************************************************************************/
encoding_syntax *get_current_encoding_syntax(const encoding_spec *spec)
{
encoding_syntax *result; /* Définition à retourner */
if (spec->syntax_count == 0)
result = NULL;
else
result = spec->syntaxes[spec->syntax_count - 1];
return result;
}
/******************************************************************************
* *
* Paramètres : spec = spécification servant de base à l'opération. *
* fd = descripteur d'un flux ouvert en écriture. *
* arch = architecture visée par l'opération. *
* id = désignation de l'identifiant d'instruction. *
* pp = pré-processeur pour les échanges de chaînes. *
* *
* Description : Traduit en code une sous-fonction de désassemblage. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool write_encoding_spec_raw_disass(const encoding_spec *spec, int fd, const char *arch, const char *id, const pre_processor *pp)
{
bool result; /* Bilan à retourner */
bool openbar; /* Syntaxe unique par défaut ? */
disass_assert *dassert; /* Eventuelles conditions */
char *suffix; /* Complément d'identifiant */
char *sid; /* Base de sous-identifiant */
int ret; /* Bilan d'une construction */
size_t i; /* Boucle de parcours */
bool op_decl; /* Suivi des déclaration #1 */
bool imm_decl; /* Suivi des déclaration #2 */
bool bad_exit; /* Ajout d'une sortie d'échec ?*/
bool quick_exit; /* Inclusion de sortie rapide ?*/
char *encoding_fc; /* Spécification d'encodage */
char *cast; /* Conversion vers le format */
result = true;
/* Détermination de la forme du code */
openbar = (spec->syntax_count == 1);
if (openbar)
{
dassert = get_assertions_for_encoding_syntax(spec->syntaxes[0]);
openbar = is_disass_assert_empty(dassert);
}
else
{
for (i = 0; i < spec->syntax_count && result; i++)
{
dassert = get_assertions_for_encoding_syntax(spec->syntaxes[0]);
if (is_disass_assert_empty(dassert))
{
fprintf(stderr, "The syntax definition #%zu has no entry conditions!\n", i);
result = false;
}
}
}
if (!result)
goto exit;
/* Déclarations préalables */
dprintf(fd, "\tGArchInstruction *result; /* Instruction créée à renvoyer*/\n");
for (i = 0; i < spec->syntax_count && result; i++)
result = mark_syntax_items(spec->syntaxes[i], spec->bits);
if (!result)
goto exit;
result = declare_used_bits_fields(spec->bits, fd);
if (!result) goto exit;
if (openbar)
{
result = declare_encoding_syntax(spec->syntaxes[0], fd, spec->bits);
if (!result) goto exit;
}
dprintf(fd, "\n");
/* Vérification que le décodage est possible */
result = check_bits_correctness(spec->bits, fd);
if (!result) goto exit;
dprintf(fd, "\n");
/* Initialisation du resultat d'un point de vue global */
if (!openbar)
{
dprintf(fd, "\tresult = NULL;\n");
dprintf(fd, "\n");
}
/* Définition des champs bruts */
result = define_used_bits_fields(spec->bits, fd);
if (!result) goto exit;
suffix = build_encoding_spec_prefix(spec);
if (suffix == NULL) goto exit;
make_string_upper(suffix);
ret = asprintf(&sid, "%s_%s", id, suffix);
free(suffix);
if (ret == -1)
goto exit;
for (i = 0; i < spec->syntax_count && result; i++)
{
if (spec->syntax_count > 1)
result = write_encoding_syntax(spec->syntaxes[i], fd, arch, spec->bits, openbar, id, sid, &i, &bad_exit);
else
result = write_encoding_syntax(spec->syntaxes[i], fd, arch, spec->bits, openbar, id, sid, NULL, &bad_exit);
}
free(sid);
if (!result)
goto exit;
/* Encodage en dernier lieu */
ret = asprintf(&encoding_fc, "g_%s_instruction_set_encoding", arch);
if (ret == -1)
goto exit;
cast = build_cast_if_needed(encoding_fc);
if (!openbar)
dprintf(fd, "\tif (result != NULL)\n");
dprintf(fd, "\t%s%s(%s(result), \"%s\");\n", openbar ? "" : "\t", encoding_fc, cast, spec->prefix);
free(cast);
free(encoding_fc);
dprintf(fd, "\n");
/* Conclusion globale */
dprintf(fd, "\treturn result;\n");
dprintf(fd, "\n");
if (bad_exit)
{
dprintf(fd, " bad_exit:\n");
dprintf(fd, "\n");
dprintf(fd, "\tg_object_unref(G_OBJECT(result));\n");
dprintf(fd, "\treturn NULL;\n");
dprintf(fd, "\n");
}
exit:
return result;
}
/******************************************************************************
* *
* Paramètres : spec = spécification servant de base à l'opération. *
* fd = descripteur d'un flux ouvert en écriture. *
* arch = architecture visée par l'opération. *
* id = identifiant unique attribué à l'instruction. *
* prefix = préfixe pour le type de définitions d'opérandes. *
* *
* Description : Traduit en code une sous-fonction de désassemblage. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool write_encoding_spec_format_disass(const encoding_spec *spec, int fd, const char *arch, const char *id, const char *prefix)
{
bool result; /* Bilan à retourner */
bool bad_exit; /* Ajout d'une sortie d'échec ?*/
conv_list *conversions; /* Conversions de la syntaxe */
decoding_rules *rules; /* Règles de la syntaxe */
/* Déclarations préalables */
dprintf(fd, "\tGArchInstruction *result; /* Instruction créée à renvoyer*/\n");
dprintf(fd, "\tSourceEndian endian; /* Boutisme lié au binaire */\n");
dprintf(fd, "\n");
/* Création de l'instruction en elle-même */
dprintf(fd, "\tresult = g_%s_instruction_new(%s);\n", arch, id);
dprintf(fd, "\n");
bad_exit = false;
assert(spec->syntax_count <= 1);
if (spec->syntax_count > 0)
{
conversions = get_conversions_in_encoding_syntax(spec->syntaxes[0]);
rules = get_rules_in_encoding_syntax(spec->syntaxes[0]);
result = write_decoding_rules(rules, CAT_CHECKED_CALL, fd, arch, spec->bits, conversions, "", &bad_exit);
if (!result) goto wesfd_exit;
result = write_decoding_rules(rules, CAT_CALL, fd, arch, spec->bits, conversions, "", &bad_exit);
if (!result) goto wesfd_exit;
}
/* Création des opérandes */
dprintf(fd, "\tendian = g_arch_processor_get_endianness(proc);\n");
dprintf(fd, "\n");
result = define_operands_loading(spec->format, fd, arch, prefix, &bad_exit);
if (!result) goto wesfd_exit;
/* Conclusion de la procédure */
dprintf(fd, "\treturn result;\n");
dprintf(fd, "\n");
if (bad_exit)
{
if (bad_exit)
dprintf(fd, " bad_exit:\n");
dprintf(fd, "\n");
dprintf(fd, "\tg_object_unref(G_OBJECT(result));\n");
dprintf(fd, "\treturn NULL;\n");
dprintf(fd, "\n");
}
wesfd_exit:
return result;
}
/******************************************************************************
* *
* Paramètres : spec = spécification servant de base à l'opération. *
* fd = descripteur d'un flux ouvert en écriture. *
* name = désignation de l'identifiant d'instruction. *
* *
* Description : Imprime les mots clefs de chaque syntaxe. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool write_encoding_spec_keywords(const encoding_spec *spec, int fd, const char *name)
{
bool result; /* Bilan à retourner */
char *suffix; /* Complément d'identifiant */
size_t i; /* Boucle de parcours */
asm_pattern *pattern; /* Définition d'assemblage */
const char *keyword; /* Mot clef principal */
suffix = build_encoding_spec_prefix(spec);
result = (suffix != NULL);
if (!result) goto exit;
make_string_upper(suffix);
for (i = 0; i < spec->syntax_count; i++)
{
/* Impression de la colonne */
if (spec->syntax_count == 1)
dprintf(fd, "\t[%s_%s]", name, suffix);
else
dprintf(fd, "\t[%s_%s_%zu]", name, suffix, i);
/* Impression des décrochages */
pattern = get_asm_pattern_in_encoding_syntax(spec->syntaxes[i]);
keyword = get_keyword_from_asm_pattern(pattern);
dprintf(fd, " = \"%s\",\n", keyword);
}
free(suffix);
exit:
return result;
}
/******************************************************************************
* *
* Paramètres : spec = spécification servant de base à l'opération. *
* fd = descripteur d'un flux ouvert en écriture. *
* name = désignation de l'identifiant d'instruction. *
* *
* Description : Imprime la définition d'un sous-identifiant pour un encodage.*
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool write_encoding_spec_subid(const encoding_spec *spec, int fd, const char *name)
{
bool result; /* Bilan à retourner */
char *suffix; /* Complément d'identifiant */
size_t i; /* Boucle de parcours */
instr_id *subid; /* Sous-identifiant de syntaxe */
unsigned int idval; /* Identifiant unique attribué */
suffix = build_encoding_spec_prefix(spec);
result = (suffix != NULL);
if (!result) goto exit;
make_string_upper(suffix);
for (i = 0; i < spec->syntax_count; i++)
{
subid = get_encoding_syntax_subid(spec->syntaxes[i]);
idval = get_instruction_id_value(subid);
if (spec->syntax_count == 1)
dprintf(fd, "\t%s_%s = %u,\n", name, suffix, idval);
else
dprintf(fd, "\t%s_%s_%zu = %u,\n", name, suffix, i, idval);
}
free(suffix);
exit:
return result;
}
/******************************************************************************
* *
* Paramètres : spec = spécification servant de base à l'opération. *
* fd = descripteur d'un flux ouvert en écriture. *
* name = désignation de l'identifiant d'instruction. *
* refine = utilisation d'un identifiant plus précis ? *
* *
* Description : Imprime d'éventuels décrochages spécifiés pour un encodage. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool write_encoding_spec_hooks(const encoding_spec *spec, int fd, const char *name, bool refine)
{
bool result; /* Bilan à retourner */
char *suffix; /* Complément d'identifiant */
size_t i; /* Boucle de parcours */
if (!has_hook_functions(spec->hooks))
result = true;
else
{
if (refine)
{
suffix = build_encoding_spec_prefix(spec);
result = (suffix != NULL);
if (!result) goto exit;
make_string_upper(suffix);
for (i = 0; i < spec->syntax_count; i++)
{
/* Impression de la colonne */
if (spec->syntax_count == 1)
dprintf(fd, "\t[%s_%s]", name, suffix);
else
dprintf(fd, "\t[%s_%s_%zu]", name, suffix, i);
/* Impression des décrochages */
dprintf(fd, " = {\n", name);
result = write_hook_functions(spec->hooks, fd);
dprintf(fd, "\t},\n", name);
}
free(suffix);
}
else
{
/* Impression de la colonne */
dprintf(fd, "\t[%s]", name);
/* Impression des décrochages */
dprintf(fd, " = {\n", name);
result = write_hook_functions(spec->hooks, fd);
dprintf(fd, "\t},\n", name);
}
}
exit:
return result;
}