/* Chrysalide - Outil d'analyse de fichiers binaires
* coder.c - lecture automatisée des spécifications d'architecture
*
* Copyright (C) 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 "coder.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* -------------------------- CONSTRUCTION SELON COMMANDES -------------------------- */
/* Conversion des chaînes en chaînes */
typedef struct _string_exch
{
const char *src; /* Chaîne à trouver */
const char *dest; /* Chaîne de remplacement */
} string_exch;
struct _encoding_spec;
/* Suivi des constructions */
struct _rented_coder
{
const char *outdir; /* Lieu d'enregistrement */
const char *arch; /* Architecture à traiter */
const char *header; /* En-tête pour les en-têtes */
string_exch *macros; /* Remplacements de chaînes */
size_t macros_count; /* Nombre de ces remplacements */
string_exch *encodings; /* Traductions d'encodages */
size_t encodings_count; /* Nombre de ces traductions */
char *copyright; /* Récupération des droits */
char *ins; /* Désignation humaine */
char *details; /* Eventuels compléments */
struct _encoding_spec *specs; /* Définitions déjà en place */
size_t specs_count; /* Nombre de ces définitions */
struct _encoding_spec *cur_spec; /* Définition courante */
};
/* Recherche l'existence d'une macro pour un remplacement. */
static const char *find_macro_in_coder(const rented_coder *, const char *);
/* --------------------------- REPRESENTATION D'ENCODAGES --------------------------- */
struct _dec_bitfield;
struct _syntax_item;
struct _conv_func;
struct _extra_rule;
/* Mémorisation d'un encodage complet */
typedef struct _encoding_spec
{
char *prefix; /* Distinction principale */
unsigned int index; /* Distinction secondaire */
struct _dec_bitfield *bitfields; /* Champs de bits détectés */
size_t bf_count; /* Nombre de ces champs */
uint64_t bits; /* Bits invariables */
uint64_t mask; /* Emplacement de ces bits */
unsigned int curpos; /* Position pendant l'analyse */
struct _syntax_item *items; /* Eléments de la syntaxe */
size_t items_count; /* Nombre de ces éléments */
struct _conv_func *conversions; /* Conversions des données */
size_t conv_count; /* Nombre de ces conversions */
struct _extra_rule *rules; /* Régles supplémentaires */
size_t rules_count; /* Nombre de ces règles */
} encoding_spec;
/* Libère de la mémoire une spécification d'encodage. */
static void free_encoding_spec(encoding_spec *);
/* ---------------------------- SYNTAXE DES INSTRUCTIONS ---------------------------- */
/* Propriétés particulières pour les opérandes */
typedef enum _SyntaxItemFlags
{
SIF_NONE = (0 << 0), /* Aucune propriété */
SIF_DECIMAL = (1 << 0) /* Affichage en décimal */
} SyntaxItemFlags;
/* Elément défini dans une syntaxe */
typedef struct _syntax_item
{
char *name; /* Désignation humaine */
bool internal; /* Enregistrement générique ? */
SyntaxItemFlags flags; /* Propriétés supplémentaires */
} syntax_item;
/* Libère de la mémoire tous les éléments de syntaxe. */
static void free_all_syntax_items_in_spec(encoding_spec *);
/* --------------------------- GESTION DES CHAMPS DE BITS --------------------------- */
/* Elément d'un mot décodé */
typedef struct _dec_bitfield
{
char *name; /* Désignation humaine */
unsigned int start; /* Position de départ */
unsigned int length; /* Taille du champ */
} dec_bitfield;
/* Recherche un champ donné dans une définition. */
static const dec_bitfield *find_named_field_in_spec(const encoding_spec *, const char *);
/* Libère de la mémoire toutes les champs de bits définis. */
static void free_all_bitfields_in_spec(encoding_spec *);
/* ---------------------------- CONVERSION DES ARGUMENTS ---------------------------- */
/* Fonction de conversion */
typedef struct _conv_func
{
char *dest; /* Variable de destination */
char *func; /* Fonction de conversion */
char *arg; /* Argument de cette fonction */
} conv_func;
/* Libère de la mémoire toutes les conversions enregistrées. */
static void free_all_conversion_in_spec(encoding_spec *);
/* Recherche une fonction converrtissant une donnée brute. */
static const conv_func *find_conversion_in_coder_spec(const encoding_spec *, const char *);
/* --------------------------- CONDITIONS ET CONSEQUENCES --------------------------- */
/* Expression d'une condition */
struct _cond_expr
{
bool is_simple; /* Sélection de champ */
union
{
struct
{
char *variable; /* Variable manipulée */
CondCompType comp; /* Type de comparaison */
char *bvalue; /* Valeur binaire comparée */
} simple;
struct
{
cond_expr *a; /* Première sous-expression */
CondOpType operator; /* Relation entre expressions */
cond_expr *b; /* Seconde sous-expression */
} composed;
};
};
/* Règles particulières */
typedef struct _extra_rule
{
cond_expr *expr; /* Expression de déclenchement */
CondActionType action; /* Conséquence d'une validation*/
char *details; /* Eventuel complément d'info. */
} extra_rule;
/* Libère de la mémoire une expression conditionnelle. */
static void free_cond_expr(cond_expr *);
/* Traduit en code une expression de condition. */
static bool write_cond_expr(const encoding_spec *, int, const cond_expr *);
/* Libère de la mémoire des règles. */
static void free_all_coder_spec_rules(encoding_spec *);
/* Traduit en code les éventuelles règles présentes. */
static bool write_coder_spec_rules(const rented_coder *, int, const encoding_spec *, bool *);
/* --------------------------- GENERATIONS DE CODE SOURCE --------------------------- */
/* Ouvre un fichier en écriture pour y placer du code. */
static int create_code_file(const rented_coder *, const char *, const char *, const char *, char, bool *);
/* Ecrit une partie des fonctions issues des spécifications. */
static bool dump_all_matching_specs_in_coder(const rented_coder *, const string_exch *, int, int);
/* Traduit en code une sous-fonction de désassemblage. */
static bool write_coder_spec_disass(const rented_coder *, int, const encoding_spec *, const char *, unsigned int);
/* ---------------------------- MANIPULATIONS DE CHAINES ---------------------------- */
/* Bascule toute une chaîne de caractères en (min|maj)uscules. */
static char *_make_string_xxx(char *, int (* fn) (int));
#define make_string_lower(str) _make_string_xxx(str, tolower)
#define make_string_upper(str) _make_string_xxx(str, toupper)
/* Traduit une chaîne en élément de fonction C. */
static char *make_callable(const char *raw, bool);
/* ---------------------------------------------------------------------------------- */
/* CONSTRUCTION SELON COMMANDES */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : - *
* *
* Description : Débute la définition d'une fonction de désassemblage. *
* *
* Retour : Gestionnaire mis en place. *
* *
* Remarques : - *
* *
******************************************************************************/
rented_coder *create_coder(void)
{
rented_coder *result; /* Structure à renvoyer */
result = (rented_coder *)calloc(1, sizeof(rented_coder));
result->cur_spec = (encoding_spec *)calloc(1, sizeof(encoding_spec));
return result;
}
/******************************************************************************
* *
* Paramètres : coder = gestion par la machine en remplacement de l'humain. *
* *
* Description : Supprime le codeur de la mémoire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void delete_coder(rented_coder *coder)
{
size_t i; /* Boucle de parcours */
if (coder->macros != NULL)
free(coder->macros);
if (coder->encodings != NULL)
free(coder->encodings);
if (coder->ins != NULL)
free(coder->ins);
if (coder->details != NULL)
free(coder->details);
for (i = 0; i < coder->specs_count; i++)
free_encoding_spec(&coder->specs[i]);
if (coder->specs != NULL)
free(coder->specs);
free_encoding_spec(coder->cur_spec);
free(coder->cur_spec);
free(coder);
}
/******************************************************************************
* *
* Paramètres : coder = gestion par la machine en remplacement de l'humain. *
* *
* Description : Détermine si les propriétés de base d'un codeur sont là. *
* *
* Retour : Bilan de l'état opérationnel. *
* *
* Remarques : - *
* *
******************************************************************************/
bool do_basic_checks_with_coder(const rented_coder *coder)
{
return (coder->outdir != NULL && coder->arch != NULL && coder->header != NULL);
}
/******************************************************************************
* *
* Paramètres : coder = gestion par la machine en remplacement de l'humain. *
* outdir = répertoire de génération des fichiers. *
* *
* Description : Spécifie le répertoire de base pour les sorties de code. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void set_coder_output_directory(rented_coder *coder, const char *outdir)
{
coder->outdir = outdir;
}
/******************************************************************************
* *
* Paramètres : coder = gestion par la machine en remplacement de l'humain. *
* arch = désignation pour le code de l'architecture lue. *
* *
* Description : Détermine l'architecture visée par les traitements. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void set_coder_arch(rented_coder *coder, const char *arch)
{
coder->arch = arch;
}
/******************************************************************************
* *
* Paramètres : coder = gestion par la machine en remplacement de l'humain. *
* header = base des définitions de protection d'en-têtes. *
* *
* Description : Définit la base des protections des fichiers d'en-tête. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void set_coder_header_base(rented_coder *coder, const char *header)
{
coder->header = header;
}
/******************************************************************************
* *
* Paramètres : coder = gestion par la machine en remplacement de l'humain. *
* src = chaîne à remplacer dans les définitions. *
* dest = chaîne de remplacement. *
* *
* Description : Enregistre une correspondance en matière d'encodage. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void register_encoding_in_coder(rented_coder *coder, const char *src, const char *dest)
{
string_exch *encoding; /* Traduction à conserver */
coder->encodings = (string_exch *)realloc(coder->encodings, ++coder->encodings_count * sizeof(string_exch));
encoding = &coder->encodings[coder->encodings_count - 1];
encoding->src = src;
encoding->dest = dest;
}
/******************************************************************************
* *
* Paramètres : coder = gestion par la machine en remplacement de l'humain. *
* src = chaîne à remplacer dans les définitions. *
* dest = chaîne de remplacement. *
* *
* Description : Constitue la matière d'un système de macros. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void define_macro_for_coder(rented_coder *coder, const char *src, const char *dest)
{
string_exch *macro; /* Nouvelle macro à constituer */
coder->macros = (string_exch *)realloc(coder->macros, ++coder->macros_count * sizeof(string_exch));
macro = &coder->macros[coder->macros_count - 1];
macro->src = src;
macro->dest = dest;
}
/******************************************************************************
* *
* Paramètres : coder = gestion par la machine en remplacement de l'humain. *
* src = chaîne à remplacer dans les définitions. *
* *
* Description : Recherche l'existence d'une macro pour un remplacement. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static const char *find_macro_in_coder(const rented_coder *coder, const char *src)
{
const char *result; /* Trouvaille à renvoyer */
size_t i; /* Boucle de parcours */
result = NULL;
for (i = 0; i < coder->macros_count && result == NULL; i++)
if (strcmp(coder->macros[i].src, src) == 0)
result = coder->macros[i].dest;
return result;
}
/******************************************************************************
* *
* Paramètres : coder = gestion par la machine en remplacement de l'humain.*
* copy = droits de copie en anglais. *
* ins = désignation humaine de l'instruction. *
* details = compléments d'informations éventuels ou NULL. *
* *
* Description : Enregistre les contours d'une instruction d'assemblage. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void save_notes_for_coder(rented_coder *coder, char *copy, char *ins, const char *details)
{
coder->copyright = copy;
coder->ins = make_string_lower(ins);
coder->details = (details != NULL ? make_callable(details, true) : strdup(""));
}
/* ---------------------------------------------------------------------------------- */
/* REPRESENTATION D'ENCODAGES */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : coder = gestion par la machine en remplacement de l'humain. *
* prefix = distinction principale entre les définitions. *
* index = distinction secondaire entre les définitions. *
* *
* Description : Enregistre une définition supplémentaire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void push_encoding_spec(rented_coder *coder, char *prefix, unsigned int index)
{
encoding_spec *spec; /* Définition à compléter */
spec = coder->cur_spec;
spec->prefix = prefix;
spec->index = index;
coder->specs = (encoding_spec *)realloc(coder->specs, ++coder->specs_count * sizeof(encoding_spec));
coder->specs[coder->specs_count - 1] = *spec;
memset(spec, 0, sizeof(encoding_spec));
}
/******************************************************************************
* *
* Paramètres : spec = spécification d'encodage en cours de libération. *
* *
* Description : Libère de la mémoire une spécification d'encodage. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void free_encoding_spec(encoding_spec *spec)
{
free_all_syntax_items_in_spec(spec);
free_all_bitfields_in_spec(spec);
free_all_conversion_in_spec(spec);
free_all_coder_spec_rules(spec);
}
/* ---------------------------------------------------------------------------------- */
/* GESTION DES CHAMPS DE BITS */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : coder = gestion par la machine en remplacement de l'humain. *
* name = désignation humaine du champ remarqué. *
* length = taille du champ à mémoriser. *
* *
* Description : Note la présence d'un champ remarquable dans une définition. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void register_named_field_in_coder(rented_coder *coder, char *name, unsigned int length)
{
encoding_spec *spec; /* Définition à compléter */
dec_bitfield *field; /* Nouveau champ à constituer */
spec = coder->cur_spec;
assert((spec->curpos + length) < 64);
spec->bitfields = (dec_bitfield *)realloc(spec->bitfields,
++spec->bf_count * sizeof(dec_bitfield));
field = &spec->bitfields[spec->bf_count - 1];
field->name = make_string_lower(name);
field->start = spec->curpos;
field->length = length;
spec->curpos += length;
}
/******************************************************************************
* *
* Paramètres : spec = spécification d'encodage à parcourir. *
* name = désignation humaine du champ à retrouver. *
* *
* Description : Recherche un champ donné dans une définition. *
* *
* Retour : Structure associée au champ trouvé ou NULL en cas d'échec. *
* *
* Remarques : - *
* *
******************************************************************************/
static const dec_bitfield *find_named_field_in_spec(const encoding_spec *spec, const char *name)
{
dec_bitfield *result; /* Champ de bits à retourner */
size_t i; /* Boucle de parcours */
result = NULL;
for (i = 0; i < spec->bf_count && result == NULL; i++)
if (strcmp(spec->bitfields[i].name, name) == 0)
result = &spec->bitfields[i];
return result;
}
/******************************************************************************
* *
* Paramètres : coder = gestion par la machine en remplacement de l'humain. *
* val = valeur du bit à prendre en compte. *
* *
* Description : Note la présence d'un bit invariable dans une définition. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void register_bit_in_coder(rented_coder *coder, int val)
{
encoding_spec *spec; /* Définition à compléter */
spec = coder->cur_spec;
assert(spec->curpos < 64);
spec->bits |= (val ? 1 : 0) << spec->curpos;
spec->mask |= 1 << spec->curpos;
spec->curpos++;
}
/******************************************************************************
* *
* Paramètres : spec = spécification d'encodage en cours de libération. *
* *
* Description : Libère de la mémoire toutes les champs de bits définis. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void free_all_bitfields_in_spec(encoding_spec *spec)
{
size_t i; /* Boucle de parcours */
for (i = 0; i < spec->bf_count; i++)
free(spec->bitfields[i].name);
if (spec->bitfields != NULL)
{
free(spec->bitfields);
spec->bitfields = NULL;
}
spec->bf_count = 0;
spec->bits = 0;
spec->mask = 0;
spec->curpos = 0;
}
/******************************************************************************
* *
* Paramètres : coder = gestion par la machine en remplacement de l'humain. *
* *
* Description : Indique le nombre de bits traités. *
* *
* Retour : Quantité, positive ou nulle. *
* *
* Remarques : - *
* *
******************************************************************************/
unsigned int count_coder_bits(const rented_coder *coder)
{
return coder->cur_spec->curpos;
}
/* ---------------------------------------------------------------------------------- */
/* SYNTAXE DES INSTRUCTIONS */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : coder = gestion par la machine en remplacement d'humain. *
* name = désignation de l'opérande dans la spécification. *
* internal = précise si l'opérand est non générique ou non. *
* *
* Description : Enregistre la présence d'un nouvel opérande. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void register_syntax_item_in_coder(rented_coder *coder, char *name, bool internal)
{
encoding_spec *spec; /* Définition à compléter */
syntax_item *item; /* Nouvelle prise en compte */
size_t len; /* Taille du nom fourni */
spec = coder->cur_spec;
spec->items = (syntax_item *)realloc(spec->items, ++spec->items_count * sizeof(syntax_item));
item = &spec->items[spec->items_count - 1];
/* Récupération des drapeaux */
item->flags = SIF_NONE;
for (len = strlen(name); len > 0; len--)
switch (name[0])
{
case '#':
item->flags |= SIF_DECIMAL;
memmove(name, name + 1, len);
break;
default:
len = 1;
break;
}
item->name = make_string_lower(name);
item->internal = internal;
}
/******************************************************************************
* *
* Paramètres : spec = spécification d'encodage en cours de libération. *
* *
* Description : Libère de la mémoire tous les éléments de syntaxe. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void free_all_syntax_items_in_spec(encoding_spec *spec)
{
size_t i; /* Boucle de parcours */
for (i = 0; i < spec->items_count; i++)
free(spec->items[i].name);
if (spec->items != NULL)
{
free(spec->items);
spec->items = NULL;
}
spec->items_count = 0;
}
/* ---------------------------------------------------------------------------------- */
/* CONVERSION DES ARGUMENTS */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : coder = gestion par la machine en remplacement de l'humain. *
* dest = désignation de la variable de destination. *
* func = nom de la fonction assurant le calcul de valeur. *
* arg = argument à fournir à cette fonction. *
* *
* Description : Enregistre la function de conversion du brut à l'utile. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void register_conversion_in_coder(rented_coder *coder, char *dest, char *func, char *arg)
{
encoding_spec *spec; /* Définition à compléter */
conv_func *conv; /* Nouvelle prise en compte */
spec = coder->cur_spec;
spec->conversions = (conv_func *)realloc(spec->conversions, ++spec->conv_count * sizeof(conv_func));
conv = &spec->conversions[spec->conv_count - 1];
conv->dest = make_string_lower(dest);
conv->func = func;
conv->arg = make_string_lower(arg);
}
/******************************************************************************
* *
* Paramètres : spec = spécification d'encodage en cours de libération. *
* *
* Description : Libère de la mémoire toutes les conversions enregistrées. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void free_all_conversion_in_spec(encoding_spec *spec)
{
size_t i; /* Boucle de parcours */
conv_func *conv; /* Conversion à traiter */
for (i = 0; i < spec->conv_count; i++)
{
conv = &spec->conversions[i];
free(conv->dest);
free(conv->func);
free(conv->arg);
}
if (spec->conversions != NULL)
{
free(spec->conversions);
spec->conversions = NULL;
}
spec->conv_count = 0;
}
/******************************************************************************
* *
* Paramètres : spec = spécification d'un encodage à consulter. *
* dest = désignation d'une variable de destination. *
* *
* Description : Recherche une fonction converrtissant une donnée brute. *
* *
* Retour : Structure de conversion trouvée ou NULL en cas d'échec. *
* *
* Remarques : - *
* *
******************************************************************************/
static const conv_func *find_conversion_in_coder_spec(const encoding_spec *spec, const char *dest)
{
const conv_func *result; /* Conversion à renvoyer */
size_t i; /* Boucle de parcours */
result = NULL;
for (i = 0; i < spec->conv_count && result == NULL; i++)
if (strcmp(spec->conversions[i].dest, dest) == 0)
result = &spec->conversions[i];
return result;
}
/* ---------------------------------------------------------------------------------- */
/* CONDITIONS ET CONSEQUENCES */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : variable = désignation de la variable à manipuler. *
* comp = type de comparaison à utiliser. *
* bvalue = valeur binaire à comparer. *
* *
* Description : Crée une expression conditionnelle simple. *
* *
* Retour : Structure mise en place. *
* *
* Remarques : - *
* *
******************************************************************************/
cond_expr *build_simple_cond_expression(char *variable, CondCompType comp, char *bvalue)
{
cond_expr *result; /* Structure à retourner */
result = (cond_expr *)calloc(1, sizeof(cond_expr));
result->is_simple = true;
result->simple.variable = make_string_lower(variable);
result->simple.comp = comp;
result->simple.bvalue = bvalue;
return result;
}
/******************************************************************************
* *
* Paramètres : a = première expression à intégrer. *
* operator = type de comparaison à utiliser. *
* b = second expression à intégrer. *
* *
* Description : Crée une expression conditionnelle composée. *
* *
* Retour : Structure mise en place. *
* *
* Remarques : - *
* *
******************************************************************************/
cond_expr *build_composed_cond_expression(cond_expr *a, CondOpType operator, cond_expr *b)
{
cond_expr *result; /* Structure à retourner */
result = (cond_expr *)calloc(1, sizeof(cond_expr));
result->is_simple = false;
result->composed.a = a;
result->composed.operator = operator;
result->composed.b = b;
return result;
}
/******************************************************************************
* *
* Paramètres : expr = représentation d'expression à traiter. *
* *
* Description : Libère de la mémoire une expression conditionnelle. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void free_cond_expr(cond_expr *expr)
{
if (expr->is_simple)
{
free(expr->simple.variable);
free(expr->simple.bvalue);
}
else
{
free_cond_expr(expr->composed.a);
free_cond_expr(expr->composed.b);
}
free(expr);
}
/******************************************************************************
* *
* Paramètres : spec = spécification d'un encodage à consulter. *
* fd = descripteur d'un flux ouvert en écriture. *
* expr = expression simple ou composée à transposer. *
* *
* Description : Traduit en code une expression de condition. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool write_cond_expr(const encoding_spec *spec, int fd, const cond_expr *expr)
{
bool result; /* Bilan à renvoyer */
const dec_bitfield *bf; /* Champ de bits de définition */
result = true;
dprintf(fd, "(");
if (expr->is_simple)
{
bf = find_named_field_in_spec(spec, expr->simple.variable);
if (bf == NULL)
{
fprintf(stderr, "Error: no bitfield defined the requested variable '%s'.\n", expr->simple.variable);
result = false;
goto wce_exit;
}
if (bf->length != strlen(expr->simple.bvalue))
{
fprintf(stderr, "Error: variable '%s' and provided value sizes do not match (%u vs %zu).\n",
expr->simple.variable, bf->length, strlen(expr->simple.bvalue));
result = false;
goto wce_exit;
}
dprintf(fd, "raw_%s", expr->simple.variable);
switch (expr->simple.comp)
{
case CCT_EQUAL:
dprintf(fd, " == ");
break;
case CCT_DIFF:
dprintf(fd, " != ");
break;
}
dprintf(fd, "b%s", expr->simple.bvalue);
}
else
{
result = write_cond_expr(spec, fd, expr->composed.a);
if (!result) goto wce_exit;
switch (expr->composed.operator)
{
case COT_AND:
dprintf(fd, " && ");
break;
case COT_OR:
dprintf(fd, " || ");
break;
}
result = write_cond_expr(spec, fd, expr->composed.b);
if (!result) goto wce_exit;
}
dprintf(fd, ")");
wce_exit:
return result;
}
/******************************************************************************
* *
* Paramètres : coder = gestion par la machine en remplacement de l'humain.*
* expr = représentation d'expression à conserver. *
* action = conséquence associée à la règle. *
* details = éventuel complément d'informations. *
* *
* Description : Ajoute une règle complète à la définition d'un codage. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void add_conditional_rule_to_coder(rented_coder *coder, cond_expr *expr, CondActionType action, const char *details)
{
encoding_spec *spec; /* Définition à compléter */
extra_rule *rule; /* Nouvelle prise en compte */
spec = coder->cur_spec;
spec->rules = (extra_rule *)realloc(spec->rules, ++spec->rules_count * sizeof(extra_rule));
rule = &spec->rules[spec->rules_count - 1];
rule->expr = expr;
rule->action = action;
rule->details = make_callable(details, false);
}
/******************************************************************************
* *
* Paramètres : spec = spécification d'encodage à traiter. *
* *
* Description : Libère de la mémoire des règles. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void free_all_coder_spec_rules(encoding_spec *spec)
{
size_t i; /* Boucle de parcours */
for (i = 0; i < spec->rules_count; i++)
{
free_cond_expr(spec->rules[i].expr);
if (spec->rules[i].details)
free(spec->rules[i].details);
}
if (spec->rules != NULL)
{
free(spec->rules);
spec->rules = NULL;
}
spec->rules_count = 0;
}
/******************************************************************************
* *
* Paramètres : coder = gestion par la machine en remplacement de l'humain. *
* fd = descripteur d'un flux ouvert en écriture. *
* spec = spécification servant de base à l'opération. *
* exit = exprime le besoin d'une voie de sortie. [OUT] *
* *
* Description : Traduit en code les éventuelles règles présentes. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool write_coder_spec_rules(const rented_coder *coder, int fd, const encoding_spec *spec, bool *exit)
{
bool result; /* Bilan à remonter */
const extra_rule *rule; /* Règle en cours d'écriture */
size_t i; /* Boucle de parcours */
result = true;
*exit = false;
for (i = 0; i < spec->rules_count; i++)
{
rule = &spec->rules[i];
dprintf(fd, "\t\tif ");
result = write_cond_expr(spec, fd, rule->expr);
if (!result) break;
dprintf(fd, "\n");
dprintf(fd, "\t\t{\n");
switch (rule->action)
{
case CAT_SEE:
if (rule->details == NULL)
{
fprintf(stderr, "Error: directive 'see' must provide additional details.\n");
result = false;
goto wcsr_exit;
}
dprintf(fd, "\t\t\tinstr = armv7_read_instr_%s", rule->details);
/* TODO : adapter les paramètres d'appel selon le 'coder' */
dprintf(fd, "(_raw);\n");
dprintf(fd, "\t\t\tgoto quick_exit;\n");
*exit = true;
break;
}
dprintf(fd, "\t\t}\n");
dprintf(fd, "\n");
}
wcsr_exit:
return result;
}
/* ---------------------------------------------------------------------------------- */
/* GENERATIONS DE CODE SOURCE */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : coder = gestion par la machine en remplacement de l'humain. *
* dir = répertoire final de destination. *
* prefix = type d'encodage à répercuter sur le nom de fichier. *
* name = nom brut du fichier à ouvrir. *
* ext = extension à donner au fichier à ouvrir. *
* exist = indique si le fichier était présent avant. [OUT] *
* *
* Description : Ouvre un fichier en écriture pour y placer du code. *
* *
* Retour : Descripteur du fichier ouvert ou -1 en cas d'échec. *
* *
* Remarques : - *
* *
******************************************************************************/
static int create_code_file(const rented_coder *coder, const char *dir, const char *prefix, const char *name, char ext, bool *exist)
{
int result; /* Descripteur à retourner */
size_t length; /* Taille du nom de fichier */
char *pathname; /* Chemin d'accès à constituer */
length = strlen(coder->outdir) + 1 + strlen(dir) + 1 + strlen(prefix) + strlen(name) + 3;
pathname = (char *)calloc(length, sizeof(char));
snprintf(pathname, length, "%s/%s/%s%s.%c", coder->outdir, dir, prefix, name, ext);
*exist = (access(pathname, W_OK) == 0);
result = open(pathname, O_WRONLY | O_CREAT | O_APPEND, 0644);
if (result == -1) perror("open()");
free(pathname);
if (!*exist && result != -1)
{
dprintf(result, "\n");
dprintf(result, "/* Chrysalide - Outil d'analyse de fichiers binaires\n");
dprintf(result, " * %s%s.%c - traduction d'instructions ARMv7\n", prefix, name, ext);
dprintf(result, " *\n");
dprintf(result, " * %s\n", coder->copyright);
dprintf(result, " *\n");
dprintf(result, " * This file is part of Chrysalide.\n");
dprintf(result, " *\n");
dprintf(result, " * Chrysalide is free software; you can redistribute it and/or modify\n");
dprintf(result, " * it under the terms of the GNU General Public License as published by\n");
dprintf(result, " * the Free Software Foundation; either version 3 of the License, or\n");
dprintf(result, " * (at your option) any later version.\n");
dprintf(result, " *\n");
dprintf(result, " * Chrysalide is distributed in the hope that it will be useful,\n");
dprintf(result, " * but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
dprintf(result, " * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n");
dprintf(result, " * GNU General Public License for more details.\n");
dprintf(result, " *\n");
dprintf(result, " * You should have received a copy of the GNU General Public License\n");
dprintf(result, " * along with Foobar. If not, see .\n");
dprintf(result, " */\n");
dprintf(result, "\n");
dprintf(result, "\n");
}
return result;
}
/******************************************************************************
* *
* Paramètres : coder = gestion par la machine en remplacement de l'humain.*
* *
* Description : Débute la définition des fonctions issues des spécifications.*
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
bool dump_all_routines_using_coder(const rented_coder *coder)
{
bool result; /* Bilan à retourner */
size_t i; /* Boucle de parcours */
string_exch *encoding; /* Type d'encodage visé */
bool exist; /* Présence du fichier visé ? */
int header_fd; /* Fichier de déclarations */
char *dash; /* Présence d'un tiret ? */
char *filename; /* Nom de fichier commun */
int code_fd; /* Fichier de définitions */
result = true;
for (i = 0; i < coder->encodings_count && result; i++)
{
encoding = &coder->encodings[i];
/* Fichier de déclarations */
header_fd = create_code_file(coder, "opcodes", encoding->dest, "opcodes", 'h', &exist);
if (header_fd == -1) return false;
if (!exist)
{
dprintf(header_fd, "#ifndef %s_OPCODES_OPCODES_H\n", coder->header);
dprintf(header_fd, "#define %s_OPCODES_OPCODES_H\n", coder->header);
dprintf(header_fd, "\n");
dprintf(header_fd, "\n");
dprintf(header_fd, "##INCLUDES##\n");
dprintf(header_fd, "\n");
dprintf(header_fd, "\n");
dprintf(header_fd, "\n");
}
/* Fichier de définitions */
dash = strchr(coder->ins, '-');
if (dash == NULL)
code_fd = create_code_file(coder, "opcodes", encoding->dest, coder->ins, 'c', &exist);
else
{
filename = strdup(coder->ins);
dash = strchr(filename, '-');
*dash = '\0';
code_fd = create_code_file(coder, "opcodes", encoding->dest, filename, 'c', &exist);
free(filename);
}
if (!exist)
{
dprintf(code_fd, "#include \"opcodes.h\"\n");
dprintf(code_fd, "\n");
dprintf(code_fd, "##INCLUDES##\n");
}
if (code_fd == -1)
{
close(header_fd);
return false;
}
/* Production de code... */
result = dump_all_matching_specs_in_coder(coder, encoding, header_fd, code_fd);
close(header_fd);
close(code_fd);
}
return result;
}
/******************************************************************************
* *
* Paramètres : coder = gestion par la machine en remplacement d'humain. *
* encoding = sélection de l'encodage à traiter. *
* hfd = flux ouvert en écriture pour les déclarations. *
* cfd = flux ouvert en écriture pour les définitions. *
* *
* Description : Ecrit une partie des fonctions issues des spécifications. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool dump_all_matching_specs_in_coder(const rented_coder *coder, const string_exch *encoding, int hfd, int cfd)
{
bool result; /* Bilan à retourner */
char *keyword; /* Mot clef appelable en code */
unsigned int wide; /* Taille des mots */
size_t i; /* Boucle de parcours */
encoding_spec *spec; /* Définition à traiter */
size_t maxlen; /* Taille à compléter */
result = true;
keyword = make_callable(coder->ins, false);
/* Recherche de la taille des mots */
wide = -1;
for (i = 0; i < coder->specs_count; i++)
{
spec = &coder->specs[i];
if (strcmp(encoding->src, spec->prefix) != 0)
continue;
wide = spec->curpos;
break;
}
/* Rien n'a été trouvé à faire... */
if (wide == -1) goto damsic_exit;
/* Désassemblage : déclaration */
dprintf(hfd, "/* Décode une instruction de type '%s'. */\n", coder->ins);
dprintf(hfd, "GArchInstruction *%s_read_instr_%s%s(uint%u_t);\n", coder->arch, keyword, coder->details, wide);
dprintf(hfd, "\n");
/* Désassemblage : définitions */
dprintf(cfd, "\n");
dprintf(cfd, "/******************************************************************************\n");
dprintf(cfd, "* *\n");
dprintf(cfd, "* Paramètres : raw = données brutes à analyser. *\n");
dprintf(cfd, "* *\n");
dprintf(cfd, "* Description : Décode une instruction de type '%s'.", coder->ins);
maxlen = 28 - strlen(coder->ins);
if (maxlen < 28)
dprintf(cfd, "%*s\n", (int)maxlen, "*");
else
dprintf(cfd, "*\n");
dprintf(cfd, " *\n");
dprintf(cfd, "* Retour : Bilan de l'opération. *\n");
dprintf(cfd, "* *\n");
dprintf(cfd, "* Remarques : - *\n");
dprintf(cfd, "* *\n");
dprintf(cfd, "******************************************************************************/\n");
dprintf(cfd, "\n");
dprintf(cfd, "GArchInstruction *%s_read_instr_%s%s(uint%u_t raw)", coder->arch, keyword, coder->details, wide);
dprintf(cfd, "\n");
dprintf(cfd, "{");
dprintf(cfd, "\n");
dprintf(cfd, "\tGArchInstruction *result; /* Instruction créée à renvoyer*/\n");
dprintf(cfd, "\n");
dprintf(cfd, "\tresult = NULL;\n");
dprintf(cfd, "\n");
for (i = 0; i < coder->specs_count && result; i++)
{
spec = &coder->specs[i];
if (strcmp(encoding->src, spec->prefix) != 0)
continue;
result = write_coder_spec_disass(coder, cfd, spec, keyword, wide);
}
dprintf(cfd, "\treturn result;\n");
dprintf(cfd, "\n");
dprintf(cfd, "}\n");
dprintf(cfd, "\n");
damsic_exit:
free(keyword);
return result;
}
/******************************************************************************
* *
* Paramètres : coder = gestion par la machine en remplacement de l'humain.*
* fd = descripteur d'un flux ouvert en écriture. *
* spec = spécification servant de base à l'opération. *
* keyword = nom clef de l'instruction utilisable dans du code. *
* wide = taille des mots manipulés (en bits). *
* *
* Description : Traduit en code une sous-fonction de désassemblage. *
* *
* Retour : Bilan de l'opération. *
* *
* Remarques : - *
* *
******************************************************************************/
static bool write_coder_spec_disass(const rented_coder *coder, int fd, const encoding_spec *spec, const char *keyword, unsigned int wide)
{
size_t i; /* Boucle de parcours */
dec_bitfield *bf; /* Accès confortable à un champ*/
bool exit; /* Inclusion de sortie rapide ?*/
syntax_item *item; /* Lien vers un opérande */
regex_t preg; /* Expression régulière */
int ret; /* Bilan d'une manipulation */
const conv_func *conv; /* Moyen de conversion du brut */
const char *callable; /* Fonction à appeler */
bool rebuild; /* Construction complexe */
regmatch_t pmatch[3]; /* Correspondances de chaînes */
size_t cmplen; /* Taille de comparaison */
char *cast; /* Macro de transtypage */
dprintf(fd, "\tGArchInstruction *%s_decode_%s%s_%s%u(uint%u_t _raw)\n",
coder->arch, keyword, coder->details, spec->prefix, spec->index, wide);
dprintf(fd, "\t{\n");
dprintf(fd, "\t\tGArchInstruction *instr;\n");
/* Déclaration des champs à retrouver */
for (i = 0; i < spec->bf_count; i++)
dprintf(fd, "\t\tuint%u_t raw_%s;\n", wide, spec->bitfields[i].name);
for (i = 0; i < spec->items_count; i++)
if (!spec->items[i].internal)
{
dprintf(fd, "\t\tGArchOperand *op;\n");
break;
}
dprintf(fd, "\n");
/* Vérification que le décodage est possible */
dprintf(fd, "\t\tif ((raw & 0x%" PRIx64 ") != 0x%" PRIx64 ") return NULL;\n", spec->mask, spec->bits);
dprintf(fd, "\n");
/* Définition des champs bruts */
for (i = 0; i < spec->bf_count; i++)
{
bf = &spec->bitfields[i];
dprintf(fd, "\t\traw_%s = (_raw >> %u) & 0x%llx;\n", bf->name, bf->start, (1ull << bf->length) - 1);
}
dprintf(fd, "\n");
/* Inclusion des éventuelles règles */
if (!write_coder_spec_rules(coder, fd, spec, &exit))
return false;
/* Création de l'instruction en elle-même */
dprintf(fd, "\t\tinstr = g_%s_instruction_new(\"%s\");\n", coder->arch, coder->ins);
dprintf(fd, "\n");
/* Création des opérandes */
bool build_arg_if_needed(const conv_func *conv)
{
/* TODO : concaténation... */
return false;
}
ret = regcomp(&preg, "(g_([a-z0-9]*)_instruction)", REG_EXTENDED);
if (ret != 0)
{
fprintf(stderr, "Internal error: bad regular expression.\n");
return false;
}
for (i = 0; i < spec->items_count; i++)
{
item = &spec->items[i];
conv = find_conversion_in_coder_spec(spec, item->name);
if (conv == NULL)
{
fprintf(stderr, "Error: expected conversion for '%s'.\n", item->name);
return false;
}
callable = find_macro_in_coder(coder, conv->func);
if (callable == NULL)
{
fprintf(stderr, "Error: expected function to store '%s'.\n", item->name);
return false;
}
rebuild = build_arg_if_needed(conv);
if (item->internal)
{
ret = regexec(&preg, callable, sizeof(pmatch) / sizeof(regmatch_t), pmatch, 0);
if (ret == REG_NOMATCH)
{
fprintf(stderr, "Internal error: bad function for dealing wih instruction: '%s'.\n", callable);
return false;
}
/**
* La variable de résultat est de type 'GArchInstruction',
* donc toute fonction différente de g_arch_instruction_*() attend un transtypage...
*/
cmplen = MAX(strlen(coder->arch), pmatch[2].rm_eo - pmatch[2].rm_so);
if (strncmp("arch", &callable[pmatch[2].rm_so], cmplen) == 0)
dprintf(fd, "\t\tif (!%s(instr, %s%s))\n",
callable, rebuild ? "" : "raw_", rebuild ? item->name : conv->arg);
else
{
cast = strndup(&callable[pmatch[1].rm_so], pmatch[1].rm_eo - pmatch[1].rm_so);
cast = make_string_upper(cast);
dprintf(fd, "\t\tif (!%s(%s(instr), %s%s))\n",
callable, cast, rebuild ? "" : "raw_", rebuild ? item->name : conv->arg);
free(cast);
}
dprintf(fd, "\t\t\tgoto bad_exit;\n");
}
else
{
if (strchr(callable, '(') == NULL)
dprintf(fd, "\t\top = %s(%s%s);\n",
callable, rebuild ? "" : "raw_", rebuild ? item->name : conv->arg);
else
dprintf(fd, "\t\top = %s%s%s);\n",
callable, rebuild ? "" : "raw_", rebuild ? item->name : conv->arg);
dprintf(fd, "\t\tif (op == NULL) goto bad_exit;\n");
dprintf(fd, "\n");
if (item->flags & SIF_DECIMAL)
dprintf(fd, "\t\tg_imm_operand_set_display(G_IMM_OPERAND(op), IOD_DEC);\n");
dprintf(fd, "\t\tg_arch_instruction_attach_extra_operand(instr, op);\n");
}
dprintf(fd, "\n");
}
regfree(&preg);
/* Conclusion de la procédure */
if (exit)
{
dprintf(fd, "\t quick_exit:\n");
dprintf(fd, "\n");
}
dprintf(fd, "\t\treturn instr;\n");
dprintf(fd, "\n");
dprintf(fd, "\t bad_exit:\n");
dprintf(fd, "\n");
dprintf(fd, "\t\tg_object_unref(G_OBJECT(instr));\n");
dprintf(fd, "\t\treturn NULL;\n");
dprintf(fd, "\n");
dprintf(fd, "\t}\n");
dprintf(fd, "\n");
dprintf(fd, "\tif (result == NULL)\n");
dprintf(fd, "\t\tresult = %s_decode_%s%s_%s%u(raw);\n",
coder->arch, keyword, coder->details, spec->prefix, spec->index);
dprintf(fd, "\n");
return true;
}
/* ---------------------------------------------------------------------------------- */
/* MANIPULATIONS DE CHAINES */
/* ---------------------------------------------------------------------------------- */
/******************************************************************************
* *
* Paramètres : str = chaîne de caractères à manipuler. [OUT] *
* *
* Description : Bascule toute une chaîne de caractères en (min|maj)uscules. *
* *
* Retour : Pointeur sur la chaîne fournie. *
* *
* Remarques : - *
* *
******************************************************************************/
static char *_make_string_xxx(char *str, int (* fn) (int))
{
size_t max; /* Empleur du parcours */
size_t i; /* Boucle de parcours */
max = strlen(str);
for (i = 0; i < max; i++)
str[i] = fn(str[i]);
return str;
}
/******************************************************************************
* *
* Paramètres : raw = données brutes en provenance de l'analyseur. *
* details = indique la nature de la chaîne à traiter. *
* *
* Description : Traduit une chaîne en élément de fonction C. *
* *
* Retour : Chaîne à libérer de la mémoire après usage. *
* *
* Remarques : - *
* *
******************************************************************************/
static char *make_callable(const char *raw, bool details)
{
char *result; /* Nom formaté à retourner */
size_t max; /* Empleur du parcours */
size_t i; /* Boucle de parcours */
result = strdup(raw);
max = strlen(result);
/* Première passe : on vire les virgules */
for (i = 0; i < max; i++)
if (result[i] == ',')
{
memmove(result + i, result + i + 1, max - i - 1);
max--;
}
result[max] = '\0';
/* Deuxième passe : on bascule en minuscules */
result = make_string_lower(result);
/* Troisième passe : on remplace les mauvais caractères */
for (i = 0; i < max; i++)
switch (result[i])
{
case 'a' ... 'z':
case '0' ... '9':
break;
case '-':
result[i] = '_';
break;
default:
result[i] = (i + 1 == max ? '\0' : '_');
break;
}
/**
* Dernière passe : on s'assure que le premier caractère n'est pas une lettre.
* On ajoute ici le préfixe '_' utilisé lors de la génération de prototypes ;
* en cas d'absence de détails, on ne se retrouve ainsi pas avec un '_' isolé.
*/
if (details && result[0] != '_')
{
max = strlen(result) + 1;
result = (char *)realloc(result, max);
memmove(result + 1, result, max - 1);
result[0] = '_';
}
return result;
}