diff options
Diffstat (limited to 'src/arch/storage.c')
-rw-r--r-- | src/arch/storage.c | 1593 |
1 files changed, 1593 insertions, 0 deletions
diff --git a/src/arch/storage.c b/src/arch/storage.c new file mode 100644 index 0000000..ac1c878 --- /dev/null +++ b/src/arch/storage.c @@ -0,0 +1,1593 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * storage.c - conservation hors mémoire vive des instructions désassemblées + * + * Copyright (C) 2018 Cyrille Bagard + * + * This file is part of Chrysalide. + * + * Chrysalide is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Chrysalide is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Foobar. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "storage.h" + + +#include <assert.h> +#include <fcntl.h> +#include <malloc.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + + +#include "instruction.h" +#include "target.h" +#include "../common/compression.h" +#include "../common/extstr.h" +#include "../common/pathname.h" +#include "../common/xdg.h" +#include "../core/global.h" +#include "../core/logs.h" +#include "../core/queue.h" +#include "../glibext/delayed-int.h" + + + +/* ----------------- CONSERVATION EXTERNE DES INSTRUCTIONS CHARGEES ----------------- */ + + +#define G_TYPE_INS_CACHING g_ins_caching_get_type() +#define G_INS_CACHING(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_INS_CACHING, GInsCaching)) +#define G_IS_INS_CACHING(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_INS_CACHING)) +#define G_INS_CACHING_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_INS_CACHING, GInsCachingClass)) +#define G_IS_INS_CACHING_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_INS_CACHING)) +#define G_INS_CACHING_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_INS_CACHING, GInsCachingClass)) + + +/* Ensembles binaires à désassembler (instance) */ +typedef struct _GInsCaching +{ + GDelayedWork parent; /* A laisser en premier */ + + GArchProcessor *proc; /* Ensemble à traiter */ + GAsmStorage *storage; /* Cache de destinartion */ + + GBinFormat *format; /* Nature de l'opération */ + + bool status; /* Bilan de l'opération */ + +} GInsCaching; + +/* Ensembles binaires à désassembler (classe) */ +typedef struct _GInsCachingClass +{ + GDelayedWorkClass parent; /* A laisser en premier */ + +} GInsCachingClass; + + +/* Indique le type défini pour les tâches d'enregistrement des instructions. */ +GType g_ins_caching_get_type(void); + +/* initialise la classe des tâches de cache d'instructions. */ +static void g_ins_caching_class_init(GInsCachingClass *); + +/* Initialise une tâche de cache d'instructions. */ +static void g_ins_caching_init(GInsCaching *); + +/* Supprime toutes les références externes. */ +static void g_ins_caching_dispose(GInsCaching *); + +/* Procède à la libération totale de la mémoire. */ +static void g_ins_caching_finalize(GInsCaching *); + +/* Crée une tâche de mise en cache de toutes les instructions. */ +static GInsCaching *g_ins_caching_new(GArchProcessor *, GAsmStorage *, GBinFormat *); + +/* Assure la conservation ou le chargement d'instructions. */ +static void g_ins_caching_process(GInsCaching *, GtkStatusStack *); + +/* Assure le chargement d'instructions en différé. */ +static void g_ins_caching_process_load(GInsCaching *, GtkStatusStack *); + +/* Assure la conservation d'instructions en différé. */ +static void g_ins_caching_process_store(GInsCaching *, GtkStatusStack *); + +/* Fournit le bilan des traitements d'instructions en différé. */ +static bool g_ins_caching_get_status(const GInsCaching *); + + + +/* ------------------- MECANISME DE SAUVEGARDE ET DE RESTAURATION ------------------- */ + + +/* Conservation d'une référence sur un type */ +typedef struct _gtype_ref_info_t +{ + GType gtype; /* Type pour la GLib */ + gpointer gclass; /* Lien vers sa classe */ + +} gtype_ref_info_t; + +/* Définition d'une conservation d'instructions d'assemblage (instance) */ +struct _GAsmStorage +{ + GObject parent; /* A laisser en premier */ + + char *id; /* Identifiant de contenu */ + + char *idx_filename; /* Fichier pour l'indexage */ + char *ins_filename; /* Fichier pour instructions */ + char *op_filename; /* Fichier pour les opérandes */ + char *tp_filename; /* Fichier pour les types */ + + int idx_fd; /* Flux pour l'indexage */ + int ins_fd; /* Flux pour les instructions */ + int op_fd; /* Flux pour les opérandes */ + int tp_fd; /* Flux pour les types */ + + /** + * La GLib n'est pas très claire sur la taille de GType : + * + * #if GLIB_SIZEOF_SIZE_T != GLIB_SIZEOF_LONG || !defined __cplusplus + * typedef gsize GType; + * #else // for historic reasons, C++ links against gulong GTypes + * typedef gulong GType; + * #endif + * + * Et : + * + * typedef unsigned $glib_size_type_define gsize; + * + * On prend le parti de réduire à 65536 types possibles dans l'enregistrement + * des objets instanciés, et on conserve ces types en tant qu'unsigned short. + */ + + gtype_ref_info_t *gtypes; /* Types des objets reconnus */ + size_t gtp_count; /* Quantité de ces objets */ + GMutex gtp_mutex; /* Contrôle d'accès à la liste */ + + GArchProcessor *proc; /* Ensemble à traiter */ + + GArchInstruction **collected; /* Liste d'instructions */ + off64_t length; /* Taille de cette liste */ + size_t count; /* Nombre de présences */ + +}; + +/* Définition d'une conservation d'instructions d'assemblage (classe) */ +struct _GAsmStorageClass +{ + GObjectClass parent; /* A laisser en premier */ + + /* Signaux */ + + void (* saved) (GAsmStorage *); + +}; + + +/* Initialise la classe des conservations d'instructions. */ +static void g_asm_storage_class_init(GAsmStorageClass *); + +/* Initialise une instance de conservation d'instructions. */ +static void g_asm_storage_init(GAsmStorage *); + +/* Supprime toutes les références externes. */ +static void g_asm_storage_dispose(GAsmStorage *); + +/* Procède à la libération totale de la mémoire. */ +static void g_asm_storage_finalize(GAsmStorage *); + +/* Indique le chemin d'accès à l'archive finale. */ +static char *g_asm_storage_get_archive_filename(const GAsmStorage *); + +/* Décompresse les fichiers de cache d'instructions. */ +static bool g_asm_storage_decompress(const GAsmStorage *); + +/* Compresse les fichiers de cache d'instructions. */ +static bool g_asm_storage_compress(const GAsmStorage *); + +/* Apprend tous les types mémorisés dans un fichier. */ +static bool g_asm_storage_read_types(GAsmStorage *); + +/* Enregistre tous les types mémorisés dans un fichier. */ +static bool g_asm_storage_write_types(GAsmStorage *); + +/* Ouvre tous les fichiers nécessaires à une opération. */ +static bool g_asm_storage_open_files(GAsmStorage *, int ); + +/* Acquitte la fin d'une tâche de sauvegarde complète. */ +static void on_cache_saving_completed(GInsCaching *, GAsmStorage *); + + + +/* ---------------------------------------------------------------------------------- */ +/* CONSERVATION EXTERNE DES INSTRUCTIONS CHARGEES */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour les tâches d'enregistrement des instructions. */ +G_DEFINE_TYPE(GInsCaching, g_ins_caching, G_TYPE_DELAYED_WORK); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des tâches de cache d'instructions. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_ins_caching_class_init(GInsCachingClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + GDelayedWorkClass *work; /* Version en classe parente */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_ins_caching_dispose; + object->finalize = (GObjectFinalizeFunc)g_ins_caching_finalize; + + work = G_DELAYED_WORK_CLASS(klass); + + work->run = (run_task_fc)g_ins_caching_process; + +} + + +/****************************************************************************** +* * +* Paramètres : caching = instance à initialiser. * +* * +* Description : Initialise une tâche de cache d'instructions. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_ins_caching_init(GInsCaching *caching) +{ + caching->status = true; + +} + + +/****************************************************************************** +* * +* Paramètres : caching = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_ins_caching_dispose(GInsCaching *caching) +{ + g_object_unref(G_OBJECT(caching->proc)); + + g_object_unref(G_OBJECT(caching->storage)); + + if (caching->format != NULL) + g_object_unref(G_OBJECT(caching->format)); + + G_OBJECT_CLASS(g_ins_caching_parent_class)->dispose(G_OBJECT(caching)); + +} + + +/****************************************************************************** +* * +* Paramètres : caching = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_ins_caching_finalize(GInsCaching *caching) +{ + G_OBJECT_CLASS(g_ins_caching_parent_class)->finalize(G_OBJECT(caching)); + +} + + +/****************************************************************************** +* * +* Paramètres : proc = gestionnaire de l'ensemble d'instructions visées. * +* storage = gestionnaire de la conservation à venir. * +* format = format binaire chargé associé à l'architecture. * +* * +* Description : Crée une tâche de mise en cache de toutes les instructions. * +* * +* Retour : Tâche créée. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static GInsCaching *g_ins_caching_new(GArchProcessor *proc, GAsmStorage *storage, GBinFormat *format) +{ + GInsCaching *result; /* Tâche à retourner */ + + result = g_object_new(G_TYPE_INS_CACHING, NULL); + + result->proc = proc; + g_object_ref(G_OBJECT(result->proc)); + + result->storage = storage; + g_object_ref(G_OBJECT(result->storage)); + + result->format = format; + + if (format != NULL) + g_object_ref(G_OBJECT(format)); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : caching = opération d'enregistrement à mener. * +* status = barre de statut à tenir informée. * +* * +* Description : Assure la conservation ou le chargement d'instructions. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_ins_caching_process(GInsCaching *caching, GtkStatusStack *status) +{ + if (caching->format != NULL) + g_ins_caching_process_load(caching, status); + + else + g_ins_caching_process_store(caching, status); + +} + + +/****************************************************************************** +* * +* Paramètres : caching = opération d'enregistrement à mener. * +* status = barre de statut à tenir informée. * +* * +* Description : Assure le chargement d'instructions en différé. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_ins_caching_process_load(GInsCaching *caching, GtkStatusStack *status) +{ + GAsmStorage *storage; /* Cache de destinartion */ + packed_buffer pbuf; /* Tampon des données à écrire */ + off64_t i; /* Boucle de parcours */ + off64_t pos; /* Position courante */ + GArchInstruction *instr; /* Instruction à traiter */ + off64_t target; /* Position dans le flux */ + + storage = caching->storage; + + init_packed_buffer(&pbuf); + + for (i = 0; i < storage->length && caching->status; i++) + { + /* Des données sont-elles présentes à cette position ? */ + + pos = lseek64(storage->idx_fd, i * sizeof(off64_t), SEEK_SET); + + if (pos != (i * sizeof(off64_t))) + { + perror("lseek64"); + caching->status = false; + break; + } + + caching->status = safe_read(storage->idx_fd, &target, sizeof(off64_t)); + + if (!caching->status) + break; + + if (target == (off64_t)-1) + continue; + + /* Chargement de l'instruction */ + + instr = g_asm_storage_get_instruction_at(storage, caching->format, i, &pbuf); + + if (instr == NULL) + caching->status = false; + + else + g_object_unref(G_OBJECT(instr)); + + } + + exit_packed_buffer(&pbuf); + +} + + +/****************************************************************************** +* * +* Paramètres : caching = opération d'enregistrement à mener. * +* status = barre de statut à tenir informée. * +* * +* Description : Assure la conservation d'instructions en différé. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_ins_caching_process_store(GInsCaching *caching, GtkStatusStack *status) +{ + GArchProcessor *proc; /* Ensemble à traiter */ + GAsmStorage *storage; /* Cache de destinartion */ + packed_buffer pbuf; /* Tampon des données à écrire */ + size_t count; /* Quantité d'instructions */ + phys_t last_phys; /* Dernière position physique */ + size_t i; /* Boucle de parcours #1 */ + GArchInstruction *instr; /* Instruction à traiter */ + off64_t pos; /* Position dans le flux */ + const mrange_t *irange; /* Emplacement de l'instruction*/ + phys_t cur_phys; /* Position physique courante */ + phys_t k; /* Boucle de parcours #2 */ + + proc = caching->proc; + storage = caching->storage; + + init_packed_buffer(&pbuf); + + g_arch_processor_lock(proc); + + count = g_arch_processor_count_instructions(proc); + + last_phys = VMPA_NO_PHYSICAL; + + for (i = 0; i < count && caching->status; i++) + { + /* Enregistrement de l'instruction */ + + instr = g_arch_processor_get_instruction(proc, i); + + caching->status = g_arch_instruction_store(instr, storage, &pbuf); + + if (caching->status) + caching->status = g_asm_storage_store_instruction_data(storage, &pbuf, &pos); + + /* Enregistrement de la position */ + + if (caching->status) + { + irange = g_arch_instruction_get_range(instr); + + cur_phys = get_phy_addr(get_mrange_addr(irange)); + + assert((last_phys == VMPA_NO_PHYSICAL && cur_phys == 0) || (cur_phys > 0 && last_phys < cur_phys)); + + if (last_phys != VMPA_NO_PHYSICAL) + for (k = last_phys; k < (cur_phys - 1) && caching->status; k++) + caching->status = safe_write(storage->idx_fd, (off64_t []) { -1 }, sizeof(off64_t)); + + caching->status = safe_write(storage->idx_fd, &pos, sizeof(off64_t)); + + last_phys = cur_phys; + + } + + g_object_unref(G_OBJECT(instr)); + + } + + g_arch_processor_unlock(proc); + + exit_packed_buffer(&pbuf); + +} + + +/****************************************************************************** +* * +* Paramètres : caching = opération d'enregistrement à mener. * +* * +* Description : Fournit le bilan des traitements d'instructions en différé. * +* * +* Retour : Bilan des opérations. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_ins_caching_get_status(const GInsCaching *caching) +{ + bool result; /* Bilan à retourner */ + + result = caching->status; + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* MECANISME DE SAUVEGARDE ET DE RESTAURATION */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour une conservation d'instructions d'assemblage. */ +G_DEFINE_TYPE(GAsmStorage, g_asm_storage, G_TYPE_OBJECT); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des conservations d'instructions. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_asm_storage_class_init(GAsmStorageClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_asm_storage_dispose; + object->finalize = (GObjectFinalizeFunc)g_asm_storage_finalize; + + g_signal_new("saved", + G_TYPE_ASM_STORAGE, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GAsmStorageClass, saved), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + +} + + +/****************************************************************************** +* * +* Paramètres : storage = instance à initialiser. * +* * +* Description : Initialise une instance de conservation d'instructions. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_asm_storage_init(GAsmStorage *storage) +{ + storage->idx_filename = NULL; + storage->ins_filename = NULL; + storage->op_filename = NULL; + storage->tp_filename = NULL; + + storage->idx_fd = -1; + storage->ins_fd = -1; + storage->op_fd = -1; + storage->tp_fd = -1; + + storage->gtypes = NULL; + storage->gtp_count = 0; + g_mutex_init(&storage->gtp_mutex); + + storage->proc = NULL; + + storage->collected = NULL; + storage->length = 0; + storage->count = 0; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_asm_storage_dispose(GAsmStorage *storage) +{ + size_t i; /* Boucle de parcours */ + + g_mutex_lock(&storage->gtp_mutex); + + for (i = 0; i < storage->gtp_count; i++) + if (storage->gtypes[i].gclass != NULL) + g_type_class_unref(storage->gtypes[i].gclass); + + g_mutex_unlock(&storage->gtp_mutex); + + g_mutex_clear(&storage->gtp_mutex); + + if (storage->proc != NULL) + g_object_unref(G_OBJECT(storage->proc)); + + for (i = 0; i < storage->length; i++) + if (storage->collected[i] != NULL) + g_object_unref(G_OBJECT(storage->collected[i])); + + G_OBJECT_CLASS(g_asm_storage_parent_class)->dispose(G_OBJECT(storage)); + +} + + +/****************************************************************************** +* * +* Paramètres : storage = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_asm_storage_finalize(GAsmStorage *storage) +{ + int ret; /* Bilan d'un appel */ + + free(storage->id); + +#define finalize_storage_file(f) \ + if (f != NULL) \ + { \ + ret = access(f, W_OK); \ + if (ret == 0) \ + { \ + ret = unlink(f); \ + if (ret != 0) perror("unlink"); \ + } \ + free(f); \ + } + + finalize_storage_file(storage->idx_filename); + finalize_storage_file(storage->ins_filename); + finalize_storage_file(storage->op_filename); + finalize_storage_file(storage->tp_filename); + + if (storage->idx_fd != -1) + close(storage->idx_fd); + + if (storage->ins_fd != -1) + close(storage->ins_fd); + + if (storage->op_fd != -1) + close(storage->op_fd); + + if (storage->gtypes != NULL) + free(storage->gtypes); + + if (storage->collected != NULL) + free(storage->collected); + + G_OBJECT_CLASS(g_asm_storage_parent_class)->finalize(G_OBJECT(storage)); + +} + + +/****************************************************************************** +* * +* Paramètres : proc = gestionnaire de l'ensemble d'instructions visées. * +* id = identifiant pour la zone d'enregistrements. * +* * +* Description : Crée le support d'une conservation d'instructions. * +* * +* Retour : Mécanismes mis en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GAsmStorage *g_asm_storage_new_compressed(GArchProcessor *proc, const gchar *id) +{ + GAsmStorage *result; /* Structure à retourner */ + char *suffix; /* Fin du nom de fichier */ + char *basedir; /* Chemin d'accès */ + bool status; /* Assurance de validité */ + + result = g_object_new(G_TYPE_ASM_STORAGE, NULL); + + result->id = strdup(id); + + result->proc = proc; + g_object_ref(G_OBJECT(proc)); + + suffix = strdup("chrysalide"); + suffix = stradd(suffix, G_DIR_SEPARATOR_S); + suffix = stradd(suffix, "cache"); + suffix = stradd(suffix, G_DIR_SEPARATOR_S); + + basedir = get_xdg_config_dir(suffix); + + free(suffix); + + status = mkpath(basedir); + if (!status) goto gasn_base_error; + + asprintf(&result->idx_filename, "%s.%s-%s", basedir, id, "index.bin"); + asprintf(&result->ins_filename, "%s.%s-%s", basedir, id, "instructions.bin"); + asprintf(&result->op_filename, "%s.%s-%s", basedir, id, "operands.bin"); + asprintf(&result->tp_filename, "%s.%s-%s", basedir, id, "types.bin"); + + free(basedir); + + return result; + + gasn_base_error: + + g_object_unref(G_OBJECT(result)); + + free(basedir); + + return NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = gestionnaire à consulter. * +* * +* Description : Indique le chemin d'accès à l'archive finale. * +* * +* Retour : Nom de fichier à libérer, ou NULL en cas d'erreur. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static char *g_asm_storage_get_archive_filename(const GAsmStorage *storage) +{ + char *result; /* Chemin d'accès à retourner */ + char *suffix; /* Fin du nom de fichier */ + + suffix = strdup("chrysalide"); + suffix = stradd(suffix, G_DIR_SEPARATOR_S); + suffix = stradd(suffix, "cache"); + suffix = stradd(suffix, G_DIR_SEPARATOR_S); + suffix = stradd(suffix, storage->id); + suffix = stradd(suffix, ".idb.tar.xz"); + + result = get_xdg_config_dir(suffix); + + free(suffix); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = gestionnaire à consulter. * +* * +* Description : Détermine si un cache d'instructions complet existe. * +* * +* Retour : Bilan de la détermination. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_asm_storage_has_cache(const GAsmStorage *storage) +{ + bool result; /* Bilan à faire remonter */ + char *filename; /* Chemin d'accès à l'archive */ + int ret; /* Résultat d'un test d'accès */ + + filename = g_asm_storage_get_archive_filename(storage); + + ret = access(filename, R_OK); + + result = (ret == 0); + + free(filename); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = gestionnaire à manipuler. * +* * +* Description : Décompresse les fichiers de cache d'instructions. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_asm_storage_decompress(const GAsmStorage *storage) +{ + bool result; /* Bilan à retourner */ + char *filename; /* Chemin d'accès à l'archive */ + struct archive *in; /* Archive à consulter */ + int ret; /* Bilan d'un appel */ + struct archive_entry *entry; /* Elément de l'archive */ + const char *path; /* Désignation d'un fichier */ + + result = false; + + filename = g_asm_storage_get_archive_filename(storage); + + in = archive_read_new(); + archive_read_support_filter_all(in); + archive_read_support_format_all(in); + + ret = archive_read_open_filename(in, filename, 10240 /* ?! */); + if (ret != ARCHIVE_OK) goto gasd_bad_archive; + + for (ret = archive_read_next_header(in, &entry); + ret == ARCHIVE_OK; + ret = archive_read_next_header(in, &entry)) + { + path = archive_entry_pathname(entry); + + if (strcmp(path, "index.bin") == 0) + { + if (!dump_archive_entry_into_file(in, entry, storage->idx_filename)) + goto gasd_exit; + } + else if (strcmp(path, "instructions.bin") == 0) + { + if (!dump_archive_entry_into_file(in, entry, storage->ins_filename)) + goto gasd_exit; + } + else if (strcmp(path, "operands.bin") == 0) + { + if (!dump_archive_entry_into_file(in, entry, storage->op_filename)) + goto gasd_exit; + } + else if (strcmp(path, "types.bin") == 0) + { + if (!dump_archive_entry_into_file(in, entry, storage->tp_filename)) + goto gasd_exit; + } + + } + + if (ret != ARCHIVE_EOF) + goto gasd_exit; + + result = true; + + gasd_exit: + + gasd_bad_archive: + + archive_read_close(in); + archive_read_free(in); + + free(filename); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = gestionnaire à manipuler. * +* * +* Description : Compresse les fichiers de cache d'instructions. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_asm_storage_compress(const GAsmStorage *storage) +{ + bool result; /* Bilan à retourner */ + char *filename; /* Chemin d'accès à l'archive */ + struct archive *out; /* Archive à constituer */ + int ret; /* Bilan d'une création */ + CPError status; /* Bilan d'une compression */ + + result = false; + + filename = g_asm_storage_get_archive_filename(storage); + + out = archive_write_new(); + archive_write_add_filter_xz(out); + archive_write_set_format_gnutar(out); + + ret = archive_write_open_filename(out, filename); + if (ret != ARCHIVE_OK) goto gasc_exit; + + status = add_file_into_archive(out, storage->idx_filename, "index.bin"); + if (status != CPE_NO_ERROR) goto gasc_exit; + + status = add_file_into_archive(out, storage->ins_filename, "instructions.bin"); + if (status != CPE_NO_ERROR) goto gasc_exit; + + status = add_file_into_archive(out, storage->op_filename, "operands.bin"); + if (status != CPE_NO_ERROR) goto gasc_exit; + + status = add_file_into_archive(out, storage->tp_filename, "types.bin"); + if (status != CPE_NO_ERROR) goto gasc_exit; + + result = true; + + gasc_exit: + + archive_write_close(out); + archive_write_free(out); + + free(filename); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = gestionnaire à compléter. * +* * +* Description : Apprend tous les types mémorisés dans un fichier. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_asm_storage_read_types(GAsmStorage *storage) +{ + bool result; /* Bilan à enregistrer */ + packed_buffer pbuf; /* Tampon des données à écrire */ + size_t i; /* Boucle de parcours */ + unsigned char len; /* Taille d'un nom de type */ + char *name; /* Désignation d'un type */ + + init_packed_buffer(&pbuf); + + result = read_packed_buffer(&pbuf, storage->tp_fd); + + if (result) + { + g_mutex_lock(&storage->gtp_mutex); + + result = extract_packed_buffer(&pbuf, &storage->gtp_count, sizeof(size_t), true); + + if (result) + storage->gtypes = (gtype_ref_info_t *)calloc(storage->gtp_count, sizeof(gtype_ref_info_t)); + + for (i = 0; i < storage->gtp_count && result; i++) + { + result = extract_packed_buffer(&pbuf, &len, sizeof(unsigned char), false); + + if (result) + { + name = (char *)malloc(len); + + result = extract_packed_buffer(&pbuf, name, len, false); + + if (result) + { + storage->gtypes[i].gtype = g_type_from_name(name); + result = (storage->gtypes[i].gtype != 0); + + if (!result) + log_variadic_message(LMT_ERROR, "Unknown type: '%s'", name); + + } + + if (result) + storage->gtypes[i].gclass = g_type_class_ref(storage->gtypes[i].gtype); + + free(name); + + } + + } + + g_mutex_unlock(&storage->gtp_mutex); + + } + + exit_packed_buffer(&pbuf); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = gestionnaire à manipuler. * +* pbuf = zone tampon à venir lire. * +* * +* Description : Crée une nouvelle instance d'objet à partir de son type. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GObject *g_asm_storage_create_object(GAsmStorage *storage, packed_buffer *pbuf) +{ + GObject *result; /* Nouvelle instance à renvoyer*/ + size_t index; /* Indice du point d'insertion */ + bool status; /* Bilan d'une récupération */ + + result = NULL; + + status = extract_packed_buffer(pbuf, &index, sizeof(size_t), true); + + if (status) + { + g_mutex_lock(&storage->gtp_mutex); + + if (index < storage->gtp_count) + result = g_object_new(storage->gtypes[index].gtype, NULL); + + g_mutex_unlock(&storage->gtp_mutex); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = gestionnaire à manipuler. * +* obj = instance dont le type est à mémoriser. * +* pbuf = zone tampon à remplir. * +* * +* Description : Sauvegarde le type d'un objet instancié. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_asm_storage_store_object_gtype(GAsmStorage *storage, GObject *obj, packed_buffer *pbuf) +{ + bool result; /* Bilan à retourner */ + GType gtype; /* Type à enregistrer */ + size_t index; /* Indice du point d'insertion */ + + gtype = G_TYPE_FROM_INSTANCE(obj); + + /** + * Pour quelques explications sur l'esquive suivante, se rapporter aux + * commentaires de g_target_operand_unserialize(). + * + * Dans la situation présente, on ne doit pas enregistrer le type dans le tampon, + * car l'opérande va relancer l'opération entière (avec un opérande temporaire), + * ce qui conduirait à l'enregistrement de deux types successifs dans les données. + */ + + if (gtype == G_TYPE_TARGET_OPERAND) + result = true; + + else + { + g_mutex_lock(&storage->gtp_mutex); + + for (index = 0; index < storage->gtp_count; index++) + if (storage->gtypes[index].gtype == gtype) + break; + + if (index == storage->gtp_count) + { + storage->gtypes = (gtype_ref_info_t *)realloc(storage->gtypes, + ++storage->gtp_count * sizeof(gtype_ref_info_t)); + + assert(storage->gtp_count > 0); + + storage->gtypes[index].gtype = gtype; + storage->gtypes[index].gclass = g_type_class_ref(gtype); + + } + + g_mutex_unlock(&storage->gtp_mutex); + + result = extend_packed_buffer(pbuf, &index, sizeof(size_t), true); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = gestionnaire à consulter. * +* * +* Description : Enregistre tous les types mémorisés dans un fichier. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_asm_storage_write_types(GAsmStorage *storage) +{ + bool result; /* Bilan à enregistrer */ + packed_buffer pbuf; /* Tampon des données à écrire */ + size_t i; /* Boucle de parcours */ + const gchar *name; /* Désignation d'un type */ + size_t len; /* Taille de ce nom */ + + init_packed_buffer(&pbuf); + + g_mutex_lock(&storage->gtp_mutex); + + result = extend_packed_buffer(&pbuf, &storage->gtp_count, sizeof(size_t), true); + + for (i = 0; i < storage->gtp_count && result; i++) + { + name = g_type_name(storage->gtypes[i].gtype); + len = strlen(name) + 1; + + if (len > (2 << (sizeof(unsigned char) * 8 - 1))) + { + log_variadic_message(LMT_ERROR, "Type name too long: '%s' (%zu bytes)", name, len); + result = false; + break; + } + + result = extend_packed_buffer(&pbuf, (unsigned char []) { len }, sizeof(unsigned char), false); + + if (result) + result = extend_packed_buffer(&pbuf, name, len, false); + + } + + if (result) + result = write_packed_buffer(&pbuf, storage->tp_fd); + + g_mutex_unlock(&storage->gtp_mutex); + + exit_packed_buffer(&pbuf); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = gestionnaire à manipuler. * +* ins = true si les données viennent d'une instruction. * +* pbuf = zone tampon à remplir. * +* pos = tête de lecture avant écriture. * +* * +* Description : Charge des données rassemblées. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool _g_asm_storage_load_data(const GAsmStorage *storage, bool ins, packed_buffer *pbuf, off64_t pos) +{ + bool result; /* Bilan à retourner */ + int fd; /* Flux ciblé */ + off64_t new; /* Nouvelle position de lecture*/ + + fd = ins ? storage->ins_fd : storage->op_fd; + + new = lseek64(fd, pos, SEEK_SET); + + if (new != pos) + result = false; + + else + { + reset_packed_buffer(pbuf); + result = read_packed_buffer(pbuf, fd); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = gestionnaire à manipuler. * +* ins = true si les données viennent d'une instruction. * +* pbuf = zone tampon à lire. * +* pos = tête de lecture avant écriture. [OUT] * +* * +* Description : Sauvegarde des données rassemblées. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool _g_asm_storage_store_data(const GAsmStorage *storage, bool ins, packed_buffer *pbuf, off64_t *pos) +{ + bool result; /* Bilan à retourner */ + int fd; /* Flux ciblé */ + + fd = ins ? storage->ins_fd : storage->op_fd; + + *pos = lseek64(fd, 0, SEEK_CUR); + + if (*pos == (off64_t)-1) + result = false; + + else + { + result = write_packed_buffer(pbuf, fd); + reset_packed_buffer(pbuf); + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = gestionnaire à manipuler. * +* flags = options d'ouverture supplémentaires. * +* * +* Description : Ouvre tous les fichiers nécessaires à une opération. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_asm_storage_open_files(GAsmStorage *storage, int flags) +{ + bool result; /* Bilan à retourner */ + +#define open_file(filename, fd) \ + ({ \ + bool __status; \ + fd = open(filename, flags | O_LARGEFILE, 0600); \ + if (fd == -1) \ + { \ + perror("open"); \ + __status = false; \ + } \ + else \ + __status = true; \ + __status; \ + }) + + result = open_file(storage->idx_filename, storage->idx_fd); + + if (result) + result = open_file(storage->ins_filename, storage->ins_fd); + + if (result) + result = open_file(storage->op_filename, storage->op_fd); + + if (result) + result = open_file(storage->tp_filename, storage->tp_fd); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = gestionnaire à manipuler. * +* format = format binaire chargé associé à l'architecture. * +* gid = groupe de travail dédié. * +* * +* Description : Lance une restauration complète d'unsauvegarde compressée. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_asm_storage_open(GAsmStorage *storage, GBinFormat *format, wgroup_id_t gid) +{ + bool result; /* Bilan à retourner */ + GInsCaching *caching; /* Tâche à faire exécuter */ + GWorkQueue *queue; /* Gestionnaire des tâches */ + GArchInstruction **list; /* Instructions rechargées */ + size_t i; /* Boucle de parcours #1 */ + size_t k; /* Boucle de parcours #2 */ + + result = g_asm_storage_decompress(storage); + + if (result) + result = g_asm_storage_open_files(storage, O_RDONLY); + + if (result) + result = g_asm_storage_read_types(storage); + + if (result) + { + storage->length = lseek64(storage->idx_fd, 0, SEEK_END); + + if (storage->length == (off64_t)-1) + { + perror("lseek64"); + result = false; + goto gaso_exit; + } + + result = (storage->length % sizeof(off64_t) == 0); + + storage->length /= sizeof(off64_t); + + } + + if (!result) + { + log_simple_message(LMT_ERROR, "Instruction cache seems corrupted..."); + goto gaso_exit; + } + + storage->collected = (GArchInstruction **)calloc(storage->length, sizeof(GArchInstruction *)); + + /** + * Cette méthode ne peut être appelée que pour un objet construit + * à partir du constructeur g_asm_storage_new_compressed(). + */ + assert(storage->proc != NULL); + + caching = g_ins_caching_new(storage->proc, storage, format); + g_object_ref(G_OBJECT(caching)); + + queue = get_work_queue(); + + g_work_queue_schedule_work(queue, G_DELAYED_WORK(caching), gid); + + g_work_queue_wait_for_completion(queue, gid); + + result = g_ins_caching_get_status(caching); + + g_object_unref(G_OBJECT(caching)); + + + if (result) + { + log_simple_message(LMT_INFO, "Successfully restored all instructions from cache!"); + + list = (GArchInstruction **)malloc(storage->count * sizeof(GArchInstruction *)); + + for (i = 0, k = 0; i < storage->length; i++) + if (storage->collected[i] != NULL) + { + list[k] = storage->collected[i]; + g_object_ref(G_OBJECT(list[k++])); + } + + assert(k == storage->count); + + g_arch_processor_set_instructions(storage->proc, list, storage->count); + + } + + else + log_simple_message(LMT_ERROR, "Failed to restore all instructions from cache!"); + + gaso_exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = gestionnaire à manipuler. * +* format = format binaire chargé associé à l'architecture. * +* index = position physique de l'instruction recherchée. * +* pbuf = tampon de lecture à disposition pour l'opération. * +* * +* Description : Fournit l'instruction correspondant à une position indicée. * +* * +* Retour : Instruction rechargée ou NULL en cas d'erreur. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GArchInstruction *g_asm_storage_get_instruction_at(GAsmStorage *storage, GBinFormat *format, off64_t index, packed_buffer *pbuf) +{ + GArchInstruction *result; /* Instruction à renvoyer */ + off64_t pos; /* Position dans le cache */ + off64_t new; /* Nouvelle position de lecture*/ + off64_t target; /* Emplacement du cache ciblé */ + bool status; /* Bilan d'une lecture */ +#ifndef NDEBUG + const mrange_t *irange; /* Emplacement de l'instruction*/ +#endif + + assert(index < storage->length); + + pos = index * sizeof(off64_t); + + if (storage->collected[index] == NULL) + { + new = lseek64(storage->idx_fd, pos, SEEK_SET); + + if (new == pos) + { + status = safe_read(storage->idx_fd, &target, sizeof(off64_t)); + + if (status) + status = g_asm_storage_load_instruction_data(storage, pbuf, target); + + if (status) + storage->collected[index] = g_arch_instruction_load(storage, format, pbuf); + + if (storage->collected[index] != NULL) + { + storage->count++; + +#ifndef NDEBUG + irange = g_arch_instruction_get_range(storage->collected[index]); + + assert(index == get_phy_addr(get_mrange_addr(irange))); +#endif + + } + + } + + } + + result = storage->collected[index]; + + if (result != NULL) + g_object_ref(G_OBJECT(result)); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = gestionnaire à manipuler. * +* * +* Description : Programme une sauvegarde complète et compressée. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_asm_storage_save(GAsmStorage *storage) +{ + bool status; /* Statut des préparatifs */ + GInsCaching *caching; /* Tâche à faire exécuter */ + GWorkQueue *queue; /* Gestionnaire des tâches */ + + status = g_asm_storage_open_files(storage, O_WRONLY | O_CREAT | O_TRUNC); + + if (!status) + log_simple_message(LMT_ERROR, "Unable to setup files for instructions caching!"); + + else + { + /** + * Cette méthode ne peut être appelée que pour un objet construit + * à partir du constructeur g_asm_storage_new_compressed(). + */ + assert(storage->proc != NULL); + + caching = g_ins_caching_new(storage->proc, storage, NULL); + + g_signal_connect(caching, "work-completed", G_CALLBACK(on_cache_saving_completed), storage); + + queue = get_work_queue(); + + g_work_queue_schedule_work(queue, G_DELAYED_WORK(caching), STORAGE_WORK_GROUP); + + } + +} + + +/****************************************************************************** +* * +* Paramètres : caching = tâche de sauvegarde menée à son terme. * +* storage = gestionnaire de conservation à la réception. * +* * +* Description : Acquitte la fin d'une tâche de sauvegarde complète. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void on_cache_saving_completed(GInsCaching *caching, GAsmStorage *storage) +{ + bool status; /* Bilan des enregistrements */ + + status = g_ins_caching_get_status(caching); + + if (status) + log_simple_message(LMT_INFO, "Successfully cached all instructions!"); + else + log_simple_message(LMT_ERROR, "Failed to cache all instructions!"); + + if (status) + status = g_asm_storage_write_types(storage); + + if (status) + { + status = g_asm_storage_compress(storage); + + if (!status) + log_simple_message(LMT_ERROR, "Failed to compress instruction cache!"); + + } + + g_signal_emit_by_name(storage, "saved"); + +} |