/* Chrysalide - Outil d'analyse de fichiers binaires * processor.c - gestion générique des architectures * * Copyright (C) 2008-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 "processor.h" #include #include #include #include "instruction-int.h" #include "processor-int.h" #include "raw.h" /* Initialise la classe générique des processeurs. */ static void g_arch_processor_class_init(GArchProcessorClass *); /* Initialise une instance de processeur d'architecture. */ static void g_arch_processor_init(GArchProcessor *); /* Supprime toutes les références externes. */ static void g_arch_processor_dispose(GArchProcessor *); /* Procède à la libération totale de la mémoire. */ static void g_arch_processor_finalize(GArchProcessor *); /* ------------------ MANIPULATIONS DES INSTRUCTIONS DESASSEMBLEES ------------------ */ /* Démarre la définition d'un nouveau groupe d'instructions. */ static void g_arch_processor_add_new_coverage(GArchProcessor *, GArchInstruction *, size_t); /* Termine la définition d'un nouveau groupe d'instructions. */ static void g_arch_processor_finish_last_coverage(GArchProcessor *, GArchInstruction *, size_t); /* Recherche rapidement un indice d'instruction via une adresse. */ static bool g_arch_processor_find_covered_index_by_address(const GArchProcessor *, const instr_coverage *, const vmpa2t *, bool, size_t *); /* Indique le type défini pour un processeur d'architecture. */ G_DEFINE_TYPE(GArchProcessor, g_arch_processor, G_TYPE_OBJECT); /****************************************************************************** * * * Paramètres : klass = classe à initialiser. * * * * Description : Initialise la classe générique des processeurs. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_arch_processor_class_init(GArchProcessorClass *klass) { GObjectClass *object; /* Autre version de la classe */ object = G_OBJECT_CLASS(klass); object->dispose = (GObjectFinalizeFunc/* ! */)g_arch_processor_dispose; object->finalize = (GObjectFinalizeFunc)g_arch_processor_finalize; } /****************************************************************************** * * * Paramètres : proc = instance à initialiser. * * * * Description : Initialise une instance de processeur d'architecture. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_arch_processor_init(GArchProcessor *proc) { #ifndef DEBUG g_atomic_int_set(&proc->locked, 0); #endif g_mutex_init(&proc->mutex); proc->coverages = NULL; proc->cov_allocated = 0; proc->cov_count = 0; } /****************************************************************************** * * * Paramètres : proc = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_arch_processor_dispose(GArchProcessor *proc) { g_mutex_clear(&proc->mutex); G_OBJECT_CLASS(g_arch_processor_parent_class)->dispose(G_OBJECT(proc)); } /****************************************************************************** * * * Paramètres : proc = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_arch_processor_finalize(GArchProcessor *proc) { G_OBJECT_CLASS(g_arch_processor_parent_class)->finalize(G_OBJECT(proc)); } /****************************************************************************** * * * Paramètres : proc = architecture visée par la procédure. * * * * Description : Fournit un contexte propre au processeur d'une architecture. * * * * Retour : Nouveau contexte mis à disposition. * * * * Remarques : - * * * ******************************************************************************/ GProcContext *g_arch_processor_get_context(const GArchProcessor *proc) { GProcContext *result; /* Contexte à retourner */ if (proc->get_ctx != NULL) result = proc->get_ctx(proc); else result = NULL; return result; } /****************************************************************************** * * * Paramètres : proc = architecture visée par la procédure. * * * * Description : Fournit un contexte lié au processeur pour une décompilation.* * * * Retour : Nouveau contexte mis à disposition. * * * * Remarques : - * * * ******************************************************************************/ #if 0 GDecContext *g_arch_processor_get_decomp_context(const GArchProcessor *proc) { GDecContext *result; /* Contexte à retourner */ if (proc->get_dec_ctx != NULL) result = proc->get_dec_ctx(proc); else result = NULL; return result; } #endif /****************************************************************************** * * * Paramètres : proc = processeur d'architecture à consulter. * * * * Description : Fournit le boustime du processeur d'une architecture. * * * * Retour : Boutisme associé au processeur. * * * * Remarques : - * * * ******************************************************************************/ SourceEndian g_arch_processor_get_endianness(const GArchProcessor *proc) { return proc->endianness; } /****************************************************************************** * * * Paramètres : proc = processeur d'architecture à consulter. * * * * Description : Fournit la taille de l'espace mémoire d'une architecture. * * * * Retour : Taille de l'espace mémoire. * * * * Remarques : - * * * ******************************************************************************/ MemoryDataSize g_arch_processor_get_memory_size(const GArchProcessor *proc) { return proc->memsize; } /****************************************************************************** * * * Paramètres : proc = processeur d'architecture à consulter. * * * * Description : Fournit la taille min. des instructions d'une architecture. * * * * Retour : Taille d'encodage des instructions * * * * Remarques : - * * * ******************************************************************************/ MemoryDataSize g_arch_processor_get_instruction_size(const GArchProcessor *proc) { return proc->inssize; } /****************************************************************************** * * * Paramètres : proc = architecture visée par la procédure. * * ctx = contexte lié à l'exécution du processeur. * * content = flux de données à analyser. * * pos = position courante dans ce flux. [OUT] * * format = format du fichier contenant le code. * * * * Description : Désassemble une instruction dans un flux de données. * * * * Retour : Instruction mise en place ou NULL en cas d'échec. * * * * Remarques : - * * * ******************************************************************************/ GArchInstruction *g_arch_processor_disassemble(const GArchProcessor *proc, GProcContext *ctx, const GBinContent *content, vmpa2t *pos, GExeFormat *format) { GArchInstruction *result; /* Instruction à renvoyer */ vmpa2t back; /* Position sauvegardée */ assert(has_phys_addr(pos)); copy_vmpa(&back, pos); result = G_ARCH_PROCESSOR_GET_CLASS(proc)->disassemble(proc, ctx, content, pos, format); if (result == NULL) copy_vmpa(pos, &back); return result; } /* ---------------------------------------------------------------------------------- */ /* RASSEMBLEMENT DES INSTRUCTIONS DESASSEMBLEES */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : proc = architecture à manipuler. * * state = nouvel état de l'accès aux instructions. * * * * Description : Protège ou lève la protection de l'accès aux instructions. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_arch_processor_lock_unlock(GArchProcessor *proc, bool state) { if (state) { g_mutex_lock(&proc->mutex); g_atomic_int_set(&proc->locked, 1); } else { g_atomic_int_set(&proc->locked, 0); g_mutex_unlock(&proc->mutex); } } /****************************************************************************** * * * Paramètres : proc = architecture à consulter via la procédure. * * * * Description : Fournit la marque de dernière modification des instructions. * * * * Retour : Marque de la dernière modification de la liste d'instruct°. * * * * Remarques : - * * * ******************************************************************************/ unsigned int g_arch_processor_get_stamp(const GArchProcessor *proc) { return proc->stamp; } /****************************************************************************** * * * Paramètres : proc = architecture visée par la procédure. * * * * Description : Compte le nombre d'instructions représentées. * * * * Retour : Nombre d'instructions présentes. * * * * Remarques : - * * * ******************************************************************************/ size_t g_arch_processor_count_instructions(const GArchProcessor *proc) { assert(g_atomic_int_get(&proc->locked) == 1); return proc->instr_count; } /****************************************************************************** * * * Paramètres : proc = architecture visée par la procédure. * * list = liste des instructions désassemblées. * * count = taille de cette liste. * * * * Description : Note les instructions désassemblées avec une architecture. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_arch_processor_set_instructions(GArchProcessor *proc, GArchInstruction **list, size_t count) { GArchInstruction *last; /* Dernière instruction traitée*/ size_t i; /* Boucle de parcours */ GArchInstruction *instr; /* Instruction à analyser */ g_arch_processor_lock(proc); proc->instructions = list; proc->instr_count = count; proc->stamp++; last = NULL; for (i = 0; i < count; i++) { instr = list[i]; /* Constitution des groupes */ if (last == NULL || g_arch_instruction_get_flags(instr) & AIF_ROUTINE_START) { if (last != NULL) g_arch_processor_finish_last_coverage(proc, last, i - 1); g_arch_processor_add_new_coverage(proc, instr, i); } last = instr; } if (last != NULL) g_arch_processor_finish_last_coverage(proc, last, proc->instr_count - 1); g_arch_processor_unlock(proc); } /****************************************************************************** * * * Paramètres : proc = architecture visée par la procédure. * * index = indice de l'instruction visée. * * * * Description : Fournit une instruction désassemblée pour une architecture. * * * * Retour : Instructions désassemblée trouvée ou NULL si aucune. * * * * Remarques : - * * * ******************************************************************************/ GArchInstruction *g_arch_processor_get_instruction(const GArchProcessor *proc, size_t index) { GArchInstruction *result; /* Instruction à retourner */ assert(g_atomic_int_get(&proc->locked) == 1); if (proc->instr_count == 0) result = NULL; else { assert(index < proc->instr_count); result = proc->instructions[index]; assert(result != NULL); g_object_ref(G_OBJECT(result)); } return result; } /* ---------------------------------------------------------------------------------- */ /* MANIPULATIONS DES INSTRUCTIONS DESASSEMBLEES */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : proc = architecture à comléter par la procédure. * * first = première instruction d'un nouveau groupe. * * start = indice de cette instruction dans l'ensemble global. * * * * Description : Démarre la définition d'un nouveau groupe d'instructions. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_arch_processor_add_new_coverage(GArchProcessor *proc, GArchInstruction *first, size_t start) { instr_coverage *coverage; /* Couverture à définir */ const mrange_t *irange; /* Couverture de l'instruction */ /* Mise à disposition de d'avantage d'espace */ if (proc->cov_allocated == proc->cov_count) { proc->cov_allocated += COV_ALLOC_BLOCK; proc->coverages = (instr_coverage *)realloc(proc->coverages, proc->cov_allocated * sizeof(instr_coverage)); } coverage = &proc->coverages[proc->cov_count++]; irange = g_arch_instruction_get_range(first); init_mrange(&coverage->range, get_mrange_addr(irange), 0); coverage->start = start; } /****************************************************************************** * * * Paramètres : proc = architecture à comléter par la procédure. * * last = dernière instruction d'un nouveau groupe. * * end = indice de cette instruction dans l'ensemble global. * * * * Description : Termine la définition d'un nouveau groupe d'instructions. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_arch_processor_finish_last_coverage(GArchProcessor *proc, GArchInstruction *last, size_t end) { instr_coverage *coverage; /* Couverture à définir */ const mrange_t *irange; /* Couverture de l'instruction */ phys_t diff; /* Ecart entre les extrémités */ coverage = &proc->coverages[proc->cov_count - 1]; irange = g_arch_instruction_get_range(last); diff = compute_vmpa_diff(get_mrange_addr(&coverage->range), get_mrange_addr(irange)); diff += get_mrange_length(irange); set_mrange_length(&coverage->range, diff); coverage->count = end - coverage->start + 1; } /****************************************************************************** * * * Paramètres : proc = processeur recensant diverses instructions. * * addr = position en mémoire ou physique à chercher. * * * * Description : Recherche un groupe d'instruction d'après son adresse. * * * * Retour : Couverture trouvée ou NULL si aucune. * * * * Remarques : - * * * ******************************************************************************/ const instr_coverage *g_arch_processor_find_coverage_by_address(const GArchProcessor *proc, const vmpa2t *addr) { instr_coverage *result; /* Trouvaille à retourner */ void *ptr; /* Résultat des recherches */ // TODO : assert locked ! int search_for_coverage_by_addr(const vmpa2t *a, const instr_coverage *c) { int status; /* Bilan d'une comparaison */ status = cmp_mrange_with_vmpa(&c->range, a); return status; } ptr = bsearch(addr, proc->coverages, proc->cov_count, sizeof(instr_coverage), (__compar_fn_t)search_for_coverage_by_addr); result = ((instr_coverage *)ptr); return result; } /****************************************************************************** * * * Paramètres : proc = processeur recensant diverses instructions. * * coverage = zone de couverture fine à fouiller. * * addr = position en mémoire ou physique à chercher. * * nearby = la recherche s'effectue-t-elle de façon stricte ? * * index = indice de l'instruction trouvée. [OUT] * * * * Description : Recherche rapidement un indice d'instruction via une adresse.* * * * Retour : Validité de l'indice fourni. * * * * Remarques : - * * * ******************************************************************************/ static bool g_arch_processor_find_covered_index_by_address(const GArchProcessor *proc, const instr_coverage *coverage, const vmpa2t *addr, bool nearby, size_t *index) { bool result; /* Bilan à faire remonter */ void *ptr; /* Résultat des recherches */ __compar_fn_t fn; /* Fonction auxiliaire adaptée */ assert(g_atomic_int_get(&proc->locked) == 1); int search_for_instr_by_addr(const vmpa2t *a, const GArchInstruction **b) { const mrange_t *range_b; /* Emplacement pour l'instr. B */ range_b = g_arch_instruction_get_range(*b); return cmp_vmpa(a, get_mrange_addr(range_b)); } int search_for_instr_by_nearby_addr(const vmpa2t *a, const GArchInstruction **b) { const mrange_t *range_b; /* Emplacement pour l'instr. B */ range_b = g_arch_instruction_get_range(*b); return cmp_mrange_with_vmpa(range_b, a); } if (nearby) fn = (__compar_fn_t)search_for_instr_by_nearby_addr; else fn = (__compar_fn_t)search_for_instr_by_addr; ptr = bsearch(addr, &proc->instructions[coverage->start], coverage->count, sizeof(GArchInstruction *), fn); if (ptr == NULL) result = false; else { result = true; *index = ((GArchInstruction **)ptr) - proc->instructions; } return result; } /****************************************************************************** * * * Paramètres : proc = processeur recensant diverses instructions. * * addr = position en mémoire ou physique à chercher. * * nearby = la recherche s'effectue-t-elle de façon stricte ? * * * * Description : Recherche une instruction d'après son adresse. * * * * Retour : Instruction trouvée à l'adresse donnée, NULL si aucune. * * * * Remarques : - * * * ******************************************************************************/ GArchInstruction *_g_arch_processor_find_instr_by_address(GArchProcessor *proc, const vmpa2t *addr, bool nearby) { GArchInstruction *result; /* Trouvaille à retourner */ const instr_coverage *coverage; /* Couverture fine à fouiller */ g_arch_processor_lock(proc); coverage = g_arch_processor_find_coverage_by_address(proc, addr); if (coverage != NULL) result = _g_arch_processor_find_covered_instr_by_address(proc, coverage, addr, nearby); else result = NULL; g_arch_processor_unlock(proc); return result; } /****************************************************************************** * * * Paramètres : proc = processeur recensant diverses instructions. * * coverage = zone de couverture fine à fouiller. * * addr = position en mémoire ou physique à chercher. * * nearby = la recherche s'effectue-t-elle de façon stricte ? * * * * Description : Recherche rapidement une instruction d'après son adresse. * * * * Retour : Instruction trouvée à l'adresse donnée, NULL si aucune. * * * * Remarques : - * * * ******************************************************************************/ GArchInstruction *_g_arch_processor_find_covered_instr_by_address(const GArchProcessor *proc, const instr_coverage *coverage, const vmpa2t *addr, bool nearby) { GArchInstruction *result; /* Trouvaille à retourner */ size_t index; /* Indice d'instruction visée */ bool valid; /* Validité de l'indice */ valid = g_arch_processor_find_covered_index_by_address(proc, coverage, addr, nearby, &index); if (valid) result = g_arch_processor_get_instruction(proc, index); else result = NULL; return result; } /****************************************************************************** * * * Paramètres : proc = processeur recensant diverses instructions. * * addr = position en mémoire ou physique à chercher. * * nearby = la recherche s'effectue-t-elle de façon stricte ? * * * * Description : Met en place un itérateur d'instruction selon une adresse. * * * * Retour : Itérateur mis en place, ou NULL si l'opération est un échec. * * * * Remarques : - * * * ******************************************************************************/ instr_iter_t *_g_arch_processor_get_iter_from_address(GArchProcessor *proc, const vmpa2t *addr, bool nearby) { instr_iter_t *result; /* Itérateur à retourner */ const instr_coverage *coverage; /* Couverture fine à fouiller */ g_arch_processor_lock(proc); coverage = g_arch_processor_find_coverage_by_address(proc, addr); if (coverage != NULL) result = _g_arch_processor_get_covered_iter_from_address(proc, coverage, addr, nearby); else result = NULL; g_arch_processor_unlock(proc); return result; } /****************************************************************************** * * * Paramètres : proc = processeur recensant diverses instructions. * * coverage = zone de couverture fine à fouiller. * * addr = position en mémoire ou physique à chercher. * * nearby = la recherche s'effectue-t-elle de façon stricte ? * * * * Description : Met en place un itérateur d'instruction selon une adresse. * * * * Retour : Itérateur mis en place, ou NULL si l'opération est un échec. * * * * Remarques : - * * * ******************************************************************************/ instr_iter_t *_g_arch_processor_get_covered_iter_from_address(GArchProcessor *proc, const instr_coverage *coverage, const vmpa2t *addr, bool nearby) { instr_iter_t *result; /* Itérateur à retourner */ size_t index; /* Indice d'instruction visée */ bool valid; /* Validité de l'indice */ valid = g_arch_processor_find_covered_index_by_address(proc, coverage, addr, nearby, &index); if (valid) result = create_instruction_iterator(proc, index); else result = NULL; return result; }