/* 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 . */ #include "storage.h" #include #include #include #include #include #include #include "instruction.h" #include "operands/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 *reg_filename; /* Fichier pour les registres */ 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 reg_fd; /* Flux pour les registres */ 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_t 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_t 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 = false;//g_arch_instruction_store__old(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->reg_filename = NULL; storage->tp_filename = NULL; storage->idx_fd = -1; storage->ins_fd = -1; storage->op_fd = -1; storage->reg_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->reg_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->reg_fd != -1) close(storage->reg_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->reg_filename, "%s.%s-%s", basedir, id, "registers.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, "registers.bin") == 0) { if (!dump_archive_entry_into_file(in, entry, storage->reg_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->reg_filename, "registers.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_t pbuf; /* Tampon des données à lire */ 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_t *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_t *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_t 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. * * type = type du fichier de destination. * * 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, StorageFileType type, packed_buffer_t *pbuf, off64_t pos) { bool result; /* Bilan à retourner */ int fd; /* Flux ciblé */ off64_t new; /* Nouvelle position de lecture*/ switch (type) { case SFT_INSTRUCTION: fd = storage->ins_fd; break; case SFT_OPERAND: fd = storage->op_fd; break; case SFT_REGISTER: fd = storage->reg_fd; break; default: fd = -1; break; } if (fd == -1) { result = false; goto type_error; } new = lseek64(fd, pos, SEEK_SET); if (new != pos) result = false; else { reset_packed_buffer(pbuf); result = read_packed_buffer(pbuf, fd); } type_error: return result; } /****************************************************************************** * * * Paramètres : storage = gestionnaire à manipuler. * * type = type du fichier de destination. * * 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, StorageFileType type, packed_buffer_t *pbuf, off64_t *pos) { bool result; /* Bilan à retourner */ int fd; /* Flux ciblé */ switch (type) { case SFT_INSTRUCTION: fd = storage->ins_fd; break; case SFT_OPERAND: fd = storage->op_fd; break; case SFT_REGISTER: fd = storage->reg_fd; break; default: fd = -1; break; } if (fd == -1) { result = false; goto type_error; } *pos = lseek64(fd, 0, SEEK_CUR); if (*pos == (off64_t)-1) result = false; else { result = write_packed_buffer(pbuf, fd); reset_packed_buffer(pbuf); } type_error: 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->reg_filename, storage->reg_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_t *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] = NULL;//g_arch_instruction_load__old(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"); }