/* 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; }