/* Chrysalide - Outil d'analyse de fichiers binaires * routine.c - manipulation des prototypes de fonctions et de variables * * Copyright (C) 2009-2017 Cyrille Bagard * * This file is part of Chrysalide. * * Chrysalide is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * Chrysalide is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Foobar. If not, see . */ #include "routine.h" #include #include #include #include "../common/extstr.h" /* Représentation générique de routine (instance) */ struct _GBinRoutine { GObject parent; /* A laisser en premier */ mrange_t range; /* Couverture mémoire */ RoutineType type; /* Type de routine */ GDataType *ret_type; /* Type retourné */ GDataType *namespace; /* Espace de noms / classe */ const char *ns_sep; /* Séparateur d'éléments */ char *name; /* Désignation humaine */ GDataType *full_name; /* Désignation très complète */ GBinVariable **args; /* Arguments de la routines */ size_t args_count; /* Nombre d'arguments */ char *cached_decl; /* Cache pour désignation #1 */ char *cached_full_decl; /* Cache pour désignation #2 */ GBinVariable **locals; /* Variables locales du code */ size_t locals_count; /* Nombre de variables locales */ GArchInstruction *instr; /* Instructions natives */ GBlockList *blocks; /* Blocs basiques d'instruct° */ //GDecInstruction *dinstr; /* Instructions décompilées */ }; /* Représentation générique de routine (classe) */ struct _GBinRoutineClass { GObjectClass parent; /* A laisser en premier */ }; /* Initialise la classe des représentation de routine. */ static void g_bin_routine_class_init(GBinRoutineClass *); /* Initialise une instance représentation de routine. */ static void g_bin_routine_init(GBinRoutine *); /* Vide le cache des descriptions humaines. */ static void g_binary_routine_reset_declarator(GBinRoutine *, bool); /* Indique le type définit pour une représentation de routine. */ G_DEFINE_TYPE(GBinRoutine, g_bin_routine, G_TYPE_OBJECT); /****************************************************************************** * * * Paramètres : klass = classe à initialiser. * * * * Description : Initialise la classe des représentation de routine. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_bin_routine_class_init(GBinRoutineClass *klass) { } /****************************************************************************** * * * Paramètres : routine = instance à initialiser. * * * * Description : Initialise une instance représentation de routine. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_bin_routine_init(GBinRoutine *routine) { } /****************************************************************************** * * * Paramètres : - * * * * Description : Crée une représentation de routine. * * * * Retour : Adresse de la structure mise en place. * * * * Remarques : - * * * ******************************************************************************/ GBinRoutine *g_binary_routine_new(void) { GBinRoutine *result; /* Structure à retourner */ result = g_object_new(G_TYPE_BIN_ROUTINE, NULL); return result; } /****************************************************************************** * * * Paramètres : type = type mis en place par la future routine. * * * * Description : Crée une représentation de routine construisant une instance.* * * * Retour : Adresse de la structure mise en place. * * * * Remarques : - * * * ******************************************************************************/ GBinRoutine *g_binary_routine_new_constructor(GDataType *type) { GBinRoutine *result; /* Structure à retourner */ result = g_object_new(G_TYPE_BIN_ROUTINE, NULL); g_binary_routine_set_name(result, _g_data_type_to_string(type, true)); return result; } /****************************************************************************** * * * Paramètres : routine = routine à effacer. * * * * Description : Supprime une représentation de routine de la mémoire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ #if 0 /* FIXME */ void g_binary_routine_finalize(GBinRoutine *routine) { size_t i; /* Boucle de parcours */ if (routine->ret_type) delete_var(routine->ret_type); if (routine->name != NULL) free(routine->name); for (i = 0; i < routine->old_args_count; i++) delete_var(routine->args_types[i]); free(routine); } #endif /****************************************************************************** * * * Paramètres : a = premières informations à consulter. * * b = secondes informations à consulter. * * * * Description : Etablit la comparaison ascendante entre deux routines. * * * * Retour : Bilan : -1 (a < b), 0 (a == b) ou 1 (a > b). * * * * Remarques : - * * * ******************************************************************************/ int g_binary_routine_compare(const GBinRoutine **a, const GBinRoutine **b) { return cmp_mrange(&(*a)->range, &(*b)->range); } /****************************************************************************** * * * Paramètres : a = premières informations à consulter. * * b = secondes informations à consulter. * * * * Description : Etablit la comparaison descendante entre deux routines. * * * * Retour : Bilan : 1 (a < b), 0 (a == b) ou -1 (a > b). * * * * Remarques : - * * * ******************************************************************************/ int g_binary_routine_rcompare(const GBinRoutine **a, const GBinRoutine **b) { return (-1) * cmp_mrange(&(*a)->range, &(*b)->range); } /****************************************************************************** * * * Paramètres : routine = routine à mettre à jour. * * range = plage mémoire ou physique déclarée. * * * * Description : Définit la couverture physique / en mémoire d'une routine. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_binary_routine_set_range(GBinRoutine *routine, const mrange_t *range) { copy_mrange(&routine->range, range); } /****************************************************************************** * * * Paramètres : routine = routine à consulter. * * * * Description : Fournit la couverture physique / en mémoire d'une routine. * * * * Retour : Plage mémoire ou physique déclarée. * * * * Remarques : - * * * ******************************************************************************/ const mrange_t *g_binary_routine_get_range(const GBinRoutine *routine) { return &routine->range; } /****************************************************************************** * * * Paramètres : routine = routine à mettre à jour. * * type = type de routine spécifié. * * * * Description : Définit le type d'une routine. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_binary_routine_set_type(GBinRoutine *routine, RoutineType type) { routine->type = type; } /****************************************************************************** * * * Paramètres : routine = routine à mettre à jour. * * namespace = instance d'appartenance. * * sep = séparateur à utiliser entre les éléments. * * * * Description : Définit le groupe d'appartenance d'une routine donnée. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_binary_routine_set_namespace(GBinRoutine *routine, GDataType *namespace, const char *sep) { routine->namespace = namespace; routine->ns_sep = sep; g_binary_routine_reset_declarator(routine, false); } /****************************************************************************** * * * Paramètres : routine = routine à mettre à jour. * * * * Description : Fournit le groupe d'appartenance d'une routine donnée. * * * * Retour : éventuelle instance d'appartenance ou NULL. * * * * Remarques : - * * * ******************************************************************************/ GDataType *g_binary_routine_get_namespace(const GBinRoutine *routine) { return routine->namespace; } /****************************************************************************** * * * Paramètres : routine = routine à mettre à jour. * * name = désignation humainement lisible. * * * * Description : Définit le nom humain d'une routine. * * * * Retour : - * * * * Remarques : Le nom ne doit pas ensuite être libéré par l'appelant ! * * * ******************************************************************************/ void g_binary_routine_set_name(GBinRoutine *routine, char *name) { if (routine->name != NULL) free(routine->name); routine->name = name; g_binary_routine_reset_declarator(routine, true); } /****************************************************************************** * * * Paramètres : routine = routine à mettre à jour. * * * * Description : Fournit le nom humain d'une routine. * * * * Retour : Désignation humainement lisible ou NULL si non définie. * * * * Remarques : - * * * ******************************************************************************/ const char *g_binary_routine_get_name(const GBinRoutine *routine) { return routine->name; } /****************************************************************************** * * * Paramètres : routine = routine à mettre à jour. * * type = désignation complète du nom de la routine. * * * * Description : Définit de façon indirecte le nom humain d'une routine. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_binary_routine_set_name_from_type(GBinRoutine *routine, GDataType *type) { if (routine->full_name != NULL) g_object_unref(G_OBJECT(routine->full_name)); routine->full_name = type; g_binary_routine_reset_declarator(routine, true); } /****************************************************************************** * * * Paramètres : routine = routine à consulter. * * * * Description : Fournit le type construisant le nom humain d'une routine. * * * * Retour : Eventuel type à l'origine du nom ou NULL. * * * * Remarques : - * * * ******************************************************************************/ GDataType *g_binary_routine_get_type_from_name(const GBinRoutine *routine) { return routine->full_name; } /****************************************************************************** * * * Paramètres : routine = routine à mettre à jour. * * type = indication sur le type de retour. * * * * Description : Définit le type de retour d'une routine. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_binary_routine_set_return_type(GBinRoutine *routine, GDataType *type) { if (routine->ret_type != NULL) g_object_unref(G_OBJECT(routine->ret_type)); routine->ret_type = type; if (type != NULL) g_object_ref(G_OBJECT(type)); } /****************************************************************************** * * * Paramètres : routine = routine à consulter. * * * * Description : Fournit le type de retour d'une routine. * * * * Retour : Indication sur le type de retour en place. * * * * Remarques : - * * * ******************************************************************************/ GDataType *g_binary_routine_get_return_type(const GBinRoutine *routine) { return routine->ret_type; } /****************************************************************************** * * * Paramètres : routine = routine à mettre à jour. * * var = variable représentant un argument supplémentaire. * * * * Description : Ajoute un argument à une routine. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_binary_routine_add_arg(GBinRoutine *routine, GBinVariable *var) { routine->args_count++; routine->args = (GBinVariable **)realloc(routine->args, routine->args_count * sizeof(GBinVariable *)); g_object_ref(G_OBJECT(var)); routine->args[routine->args_count - 1] = var; } /****************************************************************************** * * * Paramètres : routine = routine à mettre à consulter. * * * * Description : Indique le nombre d'arguments associés à une routine. * * * * Retour : Nombre d'arguments présents. * * * * Remarques : - * * * ******************************************************************************/ size_t g_binary_routine_get_args_count(const GBinRoutine *routine) { return routine->args_count; } /****************************************************************************** * * * Paramètres : routine = routine à consulter. * * index = indice de l'argument demandé. * * * * Description : Fournit un argument d'une routine. * * * * Retour : Argument demandé. * * * * Remarques : - * * * ******************************************************************************/ GBinVariable *g_binary_routine_get_arg(GBinRoutine *routine, size_t index) { return routine->args[index]; } /****************************************************************************** * * * Paramètres : routine = routine à mettre à jour. * * index = indice de l'argument à retirer; * * * * Description : Retire un argument d'une routine. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_binary_routine_remove_arg(GBinRoutine *routine, size_t index) { g_object_unref(G_OBJECT(routine->args[index])); if ((index + 1) < routine->args_count) memmove(&routine->args[index], &routine->args[index + 1], (routine->args_count - index - 1) * sizeof(GBinVariable *)); routine->args_count--; routine->args = (GBinVariable **)realloc(routine->args, routine->args_count * sizeof(GBinVariable *)); } /****************************************************************************** * * * Paramètres : routine = routine à mettre à jour. * * full = indique la portée de la réinitialisation. * * * * Description : Vide le cache des descriptions humaines. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_binary_routine_reset_declarator(GBinRoutine *routine, bool full) { if (full && routine->cached_decl != NULL) { free(routine->cached_decl); routine->cached_decl = NULL; } if (routine->cached_full_decl != NULL) { free(routine->cached_full_decl); routine->cached_full_decl = NULL; } } /****************************************************************************** * * * Paramètres : routine = routine à mettre à jour. * * full = indique si la liste des arguments est à ajouter. * * * * Description : Fournit le nom humain d'une routine. * * * * Retour : Désignation humainement lisible ou NULL si non définie. * * * * Remarques : - * * * ******************************************************************************/ const char *g_binary_routine_get_declarator(GBinRoutine *routine, bool full) { char *new; /* Nouvelle description */ char *namespace; /* Groupe d'appartenance */ size_t i; /* Boucle de parcours */ char *typestr; /* Stockage de nom temporaire */ if (routine->cached_decl == NULL) { if (routine->full_name != NULL) new = _g_data_type_to_string(routine->full_name, false); else new = routine->name; if (routine->namespace != NULL) { namespace = _g_data_type_to_string(routine->namespace, false); new = strprep(new, routine->ns_sep); new = strprep(new, namespace); free(namespace); } /* Mémorisation finale */ routine->cached_decl = new; } if (full && routine->cached_full_decl == NULL) { /* Type de retour */ if (routine->ret_type == NULL) new = strdup("??? "); else { new = _g_data_type_to_string(routine->ret_type, true); if (!g_data_type_is_pointer(routine->ret_type, true)) new = stradd(new, " "); } /* Nom de la routine */ new = stradd(new, routine->cached_decl); /* Liste des arguments */ new = stradd(new, "("); for (i = 0; i < routine->args_count; i++) { if (i > 0) new = stradd(new, ", "); typestr = g_binary_variable_to_string(routine->args[i], true); new = stradd(new, typestr); free(typestr); } new = stradd(new, ")"); /* Mémorisation finale */ routine->cached_full_decl = new; } return (full ? routine->cached_full_decl : routine->cached_decl); } /****************************************************************************** * * * Paramètres : routine = routine à mettre à jour. * * offset = position abstraite à retrouver. * * local = indique le type de variable à manipuler. * * * * Description : S'assure qu'une variable est bien associée à une routine. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_binary_routine_register_if_needed(GBinRoutine *routine, size_t offset, bool local) { #if 0 /* FIXME */ GUnknownVariable ***list; /* Liste à manipuler */ size_t *count; /* Taille de la liste */ bool found; /* Indication de présence */ size_t i; /* Boucle de parcours */ GUnknownVariable *new; /* Nouvelle variable à intégrer*/ if (local) { list = &routine->locals; count = &routine->locals_count; } else { list = &routine->args; count = &routine->args_count; } found = false; for (i = 0; i < *count && !found; i++) found = g_unknown_variable_contains_offset((*list)[i], offset); if (!found) { /* Construction */ new = g_unknown_variable_new(); g_unknown_variable_set_offset(new, offset); /* Ajout */ (*list)= (variable **)realloc(*list, ++(*count) * sizeof(GUnknownVariable *)); (*list)[*count - 1] = new; qsort(*list, *count, sizeof(GUnknownVariable *), g_unknown_variable_compare); } #endif } /****************************************************************************** * * * Paramètres : routine = routine à mettre à jour. * * offset = position abstraite à retrouver. * * local = indique le type de variable à manipuler. * * * * Description : Donne l'indice d'une variable dans la liste d'une routine. * * * * Retour : Indice de la variable dans la liste adaptée. * * * * Remarques : - * * * ******************************************************************************/ size_t g_binary_routine_get_var_index_from_offset(const GBinRoutine *routine, size_t offset, bool local) { #if 0 /* FIXME */ size_t result; /* Indice à renvoyer */ GUnknownVariable ***list; /* Liste à manipuler */ size_t *count; /* Taille de la liste */ size_t i; /* Boucle de parcours */ result = SIZE_MAX; if (local) { list = &routine->locals; count = &routine->locals_count; } else { list = &routine->args; count = &routine->args_count; } for (i = 0; i < *count && result == SIZE_MAX; i++) if (g_unknown_variable_contains_offset((*list)[i], offset)) result = i; return result; #endif return SIZE_MAX; } /****************************************************************************** * * * Paramètres : routine = routine à consulter. * * * * Description : Fournit les instructions natives correspondantes. * * * * Retour : Ensemble d'instructions décompilées ou NULL. * * * * Remarques : - * * * ******************************************************************************/ GArchInstruction *g_binary_routine_get_instructions(const GBinRoutine *routine) { return routine->instr; } /****************************************************************************** * * * Paramètres : routine = routine à mettre à jour. * * instr = série d'instructions à conserver. * * * * Description : Définit les instructions natives de la routine. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_binary_routine_set_instructions(GBinRoutine *routine, GArchInstruction *instr) { if (routine->instr != NULL) g_object_unref(G_OBJECT(routine->instr)); routine->instr = instr; } /****************************************************************************** * * * Paramètres : routine = routine à consulter. * * * * Description : Fournit les blocs basiques de la routine. * * * * Retour : Ensemble de blocs déterminés via les instructions. * * * * Remarques : - * * * ******************************************************************************/ GBlockList *g_binary_routine_get_basic_blocks(const GBinRoutine *routine) { return routine->blocks; } /****************************************************************************** * * * Paramètres : routine = routine à mettre à jour. * * blocks = ensemble de blocs déterminés via les instructions. * * * * Description : Définit les blocs basiques de la routine. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_binary_routine_set_basic_blocks(GBinRoutine *routine, GBlockList *blocks) { if (routine->blocks != NULL) g_object_unref(G_OBJECT(routine->blocks)); routine->blocks = blocks; } /****************************************************************************** * * * Paramètres : routine = routine à consulter. * * * * Description : Fournit les instructions décompilées correspondantes. * * * * Retour : Ensemble d'instructions décompilées ou NULL. * * * * Remarques : - * * * ******************************************************************************/ #if 0 GDecInstruction *g_binary_routine_get_decomp_instructions(const GBinRoutine *routine) { return routine->dinstr; } #endif /****************************************************************************** * * * Paramètres : routine = routine à mettre à jour. * * instr = série d'instructions à conserver. * * * * Description : Définit les instructions décompilées de la routine. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ #if 0 void g_binary_routine_set_decomp_instructions(GBinRoutine *routine, GDecInstruction *instr) { if (routine->dinstr != NULL) g_object_unref(G_OBJECT(routine->dinstr)); routine->dinstr = instr; } #endif /****************************************************************************** * * * Paramètres : routine = routine à consulter. * * * * Description : Décrit le prototype de la routine sous forme de caractères. * * * * Retour : Chaîne de caractères à libérer de la mémoire. * * * * Remarques : - * * * ******************************************************************************/ char *_g_binary_routine_to_string(const GBinRoutine *routine, Routine2StringOptions options) { char *result; /* Chaîne à renvoyer */ char *namespace; /* Groupe d'appartenance */ size_t i; /* Boucle de parcours */ char *typestr; /* Stockage de nom temporaire */ /* Retour de la fonction */ switch (routine->type) { case RTT_CONSTRUCTOR: result = strdup(g_binary_routine_get_name(routine)); result = stradd(result, "." /* FIXME */); break; case RTT_DESTRUCTOR: result = strdup(g_binary_routine_get_name(routine)); result = stradd(result, "::~"); break; default: /* Pour gcc */ case RTT_CLASSIC: if (routine->ret_type == NULL) result = strdup("??? "); else { result = _g_data_type_to_string(routine->ret_type, !(options & RSO_LONG_TYPE)); if (!g_data_type_is_pointer(routine->ret_type, true)) result = stradd(result, " "); } break; } /* Nom de la routine */ if (options & RSO_NAMESPACE && routine->namespace != NULL) { namespace = g_data_type_to_string(routine->namespace); result = stradd(result, namespace); result = stradd(result, "." /* FIXME */); free(namespace); } result = stradd(result, g_binary_routine_get_name(routine)); /* Liste des arguments */ result = stradd(result, "("); for (i = 0; i < routine->args_count; i++) { if (i > 0) result = stradd(result, ", "); typestr = g_binary_variable_to_string(routine->args[i], !(options & RSO_LONG_TYPE)); result = stradd(result, typestr); free(typestr); } result = stradd(result, ")"); return result; } /****************************************************************************** * * * Paramètres : routine = routine à afficher. * * lang = langage à utiliser pour la sortie humaine. * * buffer = tampon mis à disposition pour la sortie. * * * * Description : Procède à l'impression de la description d'une routine. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ #if 0 void g_binary_routine_output_info(const GBinRoutine *routine, GLangOutput *lang, GCodeBuffer *buffer) { GBufferLine *line; /* Adresse d'une ligne nouvelle*/ const char *name; /* Nom humain de la routine */ size_t len; /* Taille de ce nom */ size_t i; /* Boucle de parcours */ /* Type de retour */ line = g_lang_output_start_routine_info(lang, buffer); g_data_type_output(routine->ret_type, lang, line, true, false); g_buffer_line_append_text(line, BLC_LAST_USED, " ", 1, RTT_COMMENT, NULL); /* Nom de la routine */ name = g_binary_routine_get_name(routine); if (name != NULL) len = strlen(name); else { name = "???"; len = 3; } g_buffer_line_append_text(line, BLC_LAST_USED, name, len, RTT_COMMENT, NULL); /* Arguments éventuels... */ g_buffer_line_append_text(line, BLC_LAST_USED, "(", 1, RTT_COMMENT, NULL); for (i = 0; i < routine->args_count; i++) { if (i > 0) g_buffer_line_append_text(line, BLC_LAST_USED, ", ", 2, RTT_COMMENT, NULL); g_binary_variable_output(routine->args[i], lang, line, true, false); } g_buffer_line_append_text(line, BLC_LAST_USED, ")", 1, RTT_COMMENT, NULL); //g_lang_output_end_routine_prototype(lang, buffer, line); } #endif /****************************************************************************** * * * Paramètres : routine = routine à mettre à jour. * * lang = langage à utiliser pour la sortie humaine. * * buffer = tampon mis à disposition pour la sortie. * * body = indique le type d'impression attendu. * * * * Description : Procède à l'impression de la décompilation d'une routine. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ #if 0 void g_binary_routine_print_code(const GBinRoutine *routine, GLangOutput *lang, GCodeBuffer *buffer, bool body) { GBufferLine *line; /* Adresse d'une ligne nouvelle*/ const char *name; /* Nom humain de la routine */ size_t len; /* Taille de ce nom */ /* Type de retour */ line = g_lang_output_start_routine_prototype(lang, buffer, routine->ret_type); g_buffer_line_append_text(line, BLC_ASSEMBLY_HEAD, " ", 1, RTT_RAW, NULL); /* Nom de la routine */ name = g_binary_routine_get_name(routine); if (name != NULL) len = strlen(name); else { name = "???"; len = 3; } g_buffer_line_append_text(line, BLC_ASSEMBLY_HEAD, name, len, RTT_COMMENT, NULL); /* Corps de la routine ? */ if (!body) g_lang_output_end_routine_prototype(lang, buffer, line); else { g_lang_output_start_routine_body(lang, buffer, line); if (routine->dinstr != NULL) g_dec_instruction_print(routine->dinstr, buffer, line, lang); g_lang_output_end_routine_body(lang, buffer); } } #endif