/* Chrysalide - Outil d'analyse de fichiers binaires * program.c - support des différents formats de programmes * * Copyright (C) 2009-2024 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 Chrysalide. If not, see . */ #include "program.h" //#include //#include //#include #include "program-int.h" /* #include "preload.h" #include "../arch/processor.h" #include "../common/sort.h" #include "../core/demanglers.h" #include "../plugins/pglist.h" */ /* Initialise la classe des formats binaires génériques. */ static void g_program_format_class_init(GProgramFormatClass *); /* Initialise une instance de format binaire générique. */ static void g_program_format_init(GProgramFormat *); /* Supprime toutes les références externes. */ static void g_program_format_dispose(GProgramFormat *); /* Procède à la libération totale de la mémoire. */ static void g_program_format_finalize(GProgramFormat *); #if 0 /* Charge les plages de couvertures depuis une mémoire tampon. */ static bool g_program_format_load_start_points(GProgramFormat *, packed_buffer_t *); /* Sauvegarde les points de départ enregistrés pour un format. */ static bool g_program_format_store_start_points(GProgramFormat *, packed_buffer_t *); /* ---------------------- RASSEMBLEMENT ET GESTION DE SYMBOLES ---------------------- */ /* Retire un symbole de la collection du format binaire. */ static void _g_program_format_remove_symbol(GProgramFormat *, size_t); /* Recherche le symbole associé à une adresse. */ static bool _g_program_format_find_symbol(const GProgramFormat *, const vmpa2t *, __compar_fn_t, size_t *, GBinSymbol **); /* Recherche un symbole particulier. */ static bool __g_program_format_find_symbol(const GProgramFormat *, const void *, __compar_fn_t, size_t *, GBinSymbol **); /* ------------------ CONSERVATION DES SOUCIS DURANT LE CHARGEMENT ------------------ */ /* Charge les erreurs de chargement depuis une mémoire tampon. */ static bool g_program_format_load_errors(GProgramFormat *, packed_buffer_t *); /* Sauvegarde les erreurs de chargement dans une mémoire tampon. */ static bool g_program_format_store_errors(GProgramFormat *, packed_buffer_t *); /* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ /* Charge un format depuis une mémoire tampon. */ static bool g_program_format_load(GProgramFormat *, GObjectStorage *, packed_buffer_t *); /* Sauvegarde un format dans une mémoire tampon. */ static bool g_program_format_store(GProgramFormat *, GObjectStorage *, packed_buffer_t *); #endif /* Indique le type défini pour un format binaire générique. */ G_DEFINE_TYPE(GProgramFormat, g_program_format, G_TYPE_KNOWN_FORMAT); /****************************************************************************** * * * Paramètres : klass = classe à initialiser. * * * * Description : Initialise la classe des formats binaires génériques. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_program_format_class_init(GProgramFormatClass *klass) { GObjectClass *object; /* Autre version de la classe */ //GKnownFormatClass *known; /* Version de classe parente */ object = G_OBJECT_CLASS(klass); object->dispose = (GObjectFinalizeFunc/* ! */)g_program_format_dispose; object->finalize = (GObjectFinalizeFunc)g_program_format_finalize; #if 0 known = G_KNOWN_FORMAT_CLASS(klass); known->load = (load_known_fc)g_program_format_load; known->store = (load_known_fc)g_program_format_store; g_signal_new("symbol-added", G_TYPE_BIN_FORMAT, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(GProgramFormatClass, symbol_added), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); g_signal_new("symbol-removed", G_TYPE_BIN_FORMAT, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(GProgramFormatClass, symbol_removed), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, G_TYPE_OBJECT); #endif } /****************************************************************************** * * * Paramètres : format = instance à initialiser. * * * * Description : Initialise une instance de format binaire générique. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_program_format_init(GProgramFormat *format) { #if 0 fmt_extra_data_t *extra; /* Données insérées à modifier */ extra = GET_BIN_FORMAT_EXTRA(format); INIT_GOBJECT_EXTRA_LOCK(extra); g_rw_lock_init(&format->pt_lock); format->info = g_preload_info_new(); format->demangler = NULL; g_rw_lock_init(&format->syms_lock); #ifndef NDEBUG g_atomic_int_set(&format->sym_locked, 0); #endif format->errors = NULL; format->error_count = 0; g_mutex_init(&format->error_mutex); #ifndef NDEBUG g_atomic_int_set(&format->error_locked, 0); #endif #endif } /****************************************************************************** * * * Paramètres : format = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_program_format_dispose(GProgramFormat *format) { #if 0 size_t i; /* Boucle de parcours */ g_rw_lock_clear(&format->pt_lock); g_clear_object(&format->info); g_clear_object(&format->demangler); for (i = 0; i < format->sym_count; i++) g_clear_object(&format->symbols[i]); g_rw_lock_clear(&format->syms_lock); g_mutex_clear(&format->error_mutex); #endif G_OBJECT_CLASS(g_program_format_parent_class)->dispose(G_OBJECT(format)); } /****************************************************************************** * * * Paramètres : format = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_program_format_finalize(GProgramFormat *format) { #if 0 DisassPriorityLevel i; /* Boucle de parcours #1 */ size_t k; /* Boucle de parcours #2 */ for (i = 0; i < DPL_COUNT; i++) if (format->start_points[i] != NULL) free(format->start_points[i]); if (format->symbols != NULL) free(format->symbols); if (format->errors != NULL) { for (k = 0; k < format->error_count; k++) if (format->errors[k].desc != NULL) free(format->errors[k].desc); free(format->errors); } #endif G_OBJECT_CLASS(g_program_format_parent_class)->finalize(G_OBJECT(format)); } #if 0 /****************************************************************************** * * * Paramètres : format = format à venir modifier. * * flag = drapeau d'information complémentaire à planter. * * * * Description : Ajoute une information complémentaire à un format. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool g_program_format_set_flag(GProgramFormat *format, FormatFlag flag) { bool result; /* Bilan à retourner */ fmt_extra_data_t *extra; /* Données insérées à modifier */ extra = GET_BIN_FORMAT_EXTRA(format); LOCK_GOBJECT_EXTRA(extra); result = !(extra->flags & flag); extra->flags |= flag; UNLOCK_GOBJECT_EXTRA(extra); return result; } /****************************************************************************** * * * Paramètres : format = format à venir modifier. * * flag = drapeau d'information complémentaire à planter. * * * * Description : Retire une information complémentaire à un format. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool g_program_format_unset_flag(GProgramFormat *format, FormatFlag flag) { bool result; /* Bilan à retourner */ fmt_extra_data_t *extra; /* Données insérées à modifier */ extra = GET_BIN_FORMAT_EXTRA(format); LOCK_GOBJECT_EXTRA(extra); result = (extra->flags & flag); extra->flags &= ~flag; UNLOCK_GOBJECT_EXTRA(extra); return result; } /****************************************************************************** * * * Paramètres : format = format à venir consulter. * * flag = drapeau d'information à rechercher. * * * * Description : Détermine si un format possède un fanion particulier. * * * * Retour : Bilan de la détection. * * * * Remarques : - * * * ******************************************************************************/ bool g_program_format_has_flag(const GProgramFormat *format, FormatFlag flag) { bool result; /* Bilan à retourner */ fmt_extra_data_t *extra; /* Données insérées à modifier */ extra = GET_BIN_FORMAT_EXTRA(format); LOCK_GOBJECT_EXTRA(extra); result = (extra->flags & flag); UNLOCK_GOBJECT_EXTRA(extra); return result; } /****************************************************************************** * * * Paramètres : format = format à venir consulter. * * * * Description : Fournit les particularités du format. * * * * Retour : Somme de tous les fanions associés au format. * * * * Remarques : - * * * ******************************************************************************/ FormatFlag g_program_format_get_flags(const GProgramFormat *format) { FormatFlag result; /* Fanions à retourner */ fmt_extra_data_t *extra; /* Données insérées à modifier */ extra = GET_BIN_FORMAT_EXTRA(format); LOCK_GOBJECT_EXTRA(extra); result = (extra->flags & FFL_MASK); UNLOCK_GOBJECT_EXTRA(extra); return result; } #endif /****************************************************************************** * * * Paramètres : format = description de l'exécutable à consulter. * * * * Description : Indique le boutisme employé par le format binaire analysé. * * * * Retour : Boutisme associé au format. * * * * Remarques : - * * * ******************************************************************************/ SourceEndian g_program_format_get_endianness(const GProgramFormat *format) { SourceEndian result; /* Boutisme à retourner */ GProgramFormatClass *class; /* Classe de l'instance */ class = G_PROGRAM_FORMAT_GET_CLASS(format); result = class->get_endian(format); return result; } #if 0 /****************************************************************************** * * * Paramètres : format = description de l'exécutable à compléter. * * pt = point de l'espace mémoire à considérer. * * level = indication de priorité et d'origine de l'adresse. * * * * Description : Enregistre une adresse comme début d'une zone de code. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_program_format_register_code_point(GProgramFormat *format, virt_t pt, DisassPriorityLevel level) { assert(level < DPL_COUNT); g_rw_lock_writer_lock(&format->pt_lock); if (format->pt_count[level] == format->pt_allocated[level]) { format->pt_allocated[level] += EXTRA_POINT_BLOCK; format->start_points[level] = realloc(format->start_points[level], format->pt_allocated[level] * sizeof(virt_t)); } format->start_points[level][format->pt_count[level]++] = pt; g_rw_lock_writer_unlock(&format->pt_lock); } /****************************************************************************** * * * Paramètres : proc = architecture concernée par la procédure. * * pbuf = zone tampon à vider. * * * * Description : Charge les plages de couvertures depuis une mémoire tampon. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ static bool g_program_format_load_start_points(GProgramFormat *format, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ DisassPriorityLevel i; /* Boucle de parcours #1 */ uleb128_t count; /* Quantité de points présents */ size_t k; /* Boucle de parcours #2 */ uleb128_t value; /* Valeur ULEB128 à charger */ result = true; g_rw_lock_writer_lock(&format->pt_lock); for (i = 0; i < DPL_COUNT && result; i++) { result = unpack_uleb128(&count, pbuf); if (!result) break; format->pt_allocated[i] = count; format->pt_count[i] = count; format->start_points[i] = calloc(format->pt_count[i], sizeof(virt_t)); for (k = 0; k < format->pt_count[i] && result; k++) { result = unpack_uleb128(&value, pbuf); if (!result) break; format->start_points[i][k] = value; } } g_rw_lock_writer_unlock(&format->pt_lock); return result; } /****************************************************************************** * * * Paramètres : format = description de l'exécutable à consulter. * * pbuf = zone tampon à remplir. * * * * Description : Sauvegarde les points de départ enregistrés pour un format. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ static bool g_program_format_store_start_points(GProgramFormat *format, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ DisassPriorityLevel i; /* Boucle de parcours #1 */ size_t count; /* Quantité de points présents */ size_t k; /* Boucle de parcours #2 */ result = true; g_rw_lock_writer_lock(&format->pt_lock); for (i = 0; i < DPL_COUNT && result; i++) { count = format->pt_count[i]; result = pack_uleb128((uleb128_t []){ count }, pbuf); for (k = 0; k < count && result; k++) result = pack_uleb128((uleb128_t []){ format->start_points[i][k] }, pbuf); } g_rw_lock_writer_unlock(&format->pt_lock); return result; } /****************************************************************************** * * * Paramètres : format = description de l'exécutable à consulter. * * ctx = contexte de désassemblage à préparer. * * status = barre de statut à tenir informée. * * * * Description : Intègre dans un contexte les informations tirées d'un format.* * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_program_format_preload_disassembling_context(GProgramFormat *format, GProcContext *ctx, GtkStatusStack *status) { g_preload_info_copy(format->info, G_PRELOAD_INFO(ctx)); } /****************************************************************************** * * * Paramètres : format = description de l'exécutable à consulter. * * ctx = contexte de désassemblage à préparer. * * status = barre de statut à tenir informée. * * * * Description : Définit les points de départ d'un contexte de désassemblage. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_program_format_activate_disassembling_context(GProgramFormat *format, GProcContext *ctx, GtkStatusStack *status) { DisassPriorityLevel i; /* Boucle de parcours #1 */ size_t k; /* Boucle de parcours #2 */ g_rw_lock_reader_lock(&format->pt_lock); for (i = 0; i < DPL_COUNT; i++) for (k = 0; k < format->pt_count[i]; k++) g_proc_context_push_drop_point(ctx, i, format->start_points[i][k]); g_rw_lock_reader_unlock(&format->pt_lock); } /* ---------------------------------------------------------------------------------- */ /* DECODAGE DE SYMBOLES */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : format = format binaire à consulter pour l'opération. * * * * Description : Fournit le décodeur de symboles privilégié pour un format. * * * * Retour : Décodeur préféré ou NULL s'il n'est pas renseigné. * * * * Remarques : - * * * ******************************************************************************/ GCompDemangler *g_program_format_get_demangler(const GProgramFormat *format) { GCompDemangler *result; /* Décodeur à retourner */ result = format->demangler; if (result != NULL) g_object_ref(G_OBJECT(result)); return result; } /****************************************************************************** * * * Paramètres : format = format binaire à consulter pour l'opération. * * desc = chaîne de caractères à décoder. * * * * Description : Décode une chaîne de caractères donnée en type. * * * * Retour : Instance obtenue ou NULL en cas d'échec. * * * * Remarques : - * * * ******************************************************************************/ GDataType *g_program_format_decode_type(const GProgramFormat *format, const char *desc) { GDataType *result; /* Construction à remonter */ GCompDemangler *demangler; /* Accès plus lisible */ demangler = format->demangler; if (demangler != NULL) result = g_compiler_demangler_decode_type(demangler, desc); else result = NULL; return result; } /****************************************************************************** * * * Paramètres : format = format binaire à consulter pour l'opération. * * desc = chaîne de caractères à décoder. * * * * Description : Décode une chaîne de caractères donnée en routine. * * * * Retour : Instance obtenue ou NULL en cas d'échec. * * * * Remarques : - * * * ******************************************************************************/ GBinRoutine *g_program_format_decode_routine(const GProgramFormat *format, const char *desc) { GBinRoutine *result; /* Construction à remonter */ GCompDemangler *demangler; /* Accès plus lisible */ demangler = format->demangler; if (demangler != NULL) result = g_compiler_demangler_decode_routine(demangler, desc); else result = NULL; if (result == NULL) { result = g_binary_routine_new(); g_binary_routine_set_name(result, strdup(desc)); } return result; } /* ---------------------------------------------------------------------------------- */ /* RASSEMBLEMENT ET GESTION DE SYMBOLES */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : format = architecture à manipuler. * * state = nouvel état de l'accès aux symboles. * * * * Description : Protège ou lève la protection de l'accès aux symboles. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_program_format_lock_unlock_symbols_rd(GProgramFormat *format, bool state) { #ifndef NDEBUG gint test; /* Test de valeur courante */ #endif if (state) { g_rw_lock_reader_lock(&format->syms_lock); #ifndef NDEBUG g_atomic_int_inc(&format->sym_locked); #endif } else { #ifndef NDEBUG test = g_atomic_int_add(&format->sym_locked, -1); assert(test > 0); #endif g_rw_lock_reader_unlock(&format->syms_lock); } } /****************************************************************************** * * * Paramètres : format = architecture à manipuler. * * state = nouvel état de l'accès aux symboles. * * * * Description : Protège ou lève la protection de l'accès aux symboles. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_program_format_lock_unlock_symbols_wr(GProgramFormat *format, bool state) { if (state) { g_rw_lock_writer_lock(&format->syms_lock); #ifndef NDEBUG g_atomic_int_set(&format->sym_locked, 1); #endif } else { #ifndef NDEBUG g_atomic_int_set(&format->sym_locked, 0); #endif g_rw_lock_writer_unlock(&format->syms_lock); } } /****************************************************************************** * * * Paramètres : format = architecture à consulter via la procédure. * * * * Description : Assure qu'un verrou est bien posé pour l'accès aux symboles. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ #ifndef NDEBUG void g_program_format_check_for_symbols_lock(const GProgramFormat *format) { assert(g_atomic_int_get(&format->sym_locked) > 0); } #endif /****************************************************************************** * * * Paramètres : format = architecture à consulter via la procédure. * * * * Description : Fournit la marque de dernière modification des symboles. * * * * Retour : Marque de la dernière modification de la liste de symboles. * * * * Remarques : - * * * ******************************************************************************/ unsigned int g_program_format_get_symbols_stamp(const GProgramFormat *format) { return format->sym_stamp; } /****************************************************************************** * * * Paramètres : format = format visé par la procédure. * * * * Description : Compte le nombre de symboles représentés. * * * * Retour : Nombre de symboles présents. * * * * Remarques : - * * * ******************************************************************************/ size_t g_program_format_count_symbols(const GProgramFormat *format) { assert(g_atomic_int_get(&format->sym_locked) > 0); return format->sym_count; } /****************************************************************************** * * * Paramètres : format = format visé par la procédure. * * index = indice du symbole visé. * * * * Description : Fournit un symbole lié à un format. * * * * Retour : Symbole conservé trouvé ou NULL si aucun. * * * * Remarques : - * * * ******************************************************************************/ GBinSymbol *g_program_format_get_symbol(const GProgramFormat *format, size_t index) { GBinSymbol *result; /* Symbole à retourner */ assert(g_atomic_int_get(&format->sym_locked) > 0); if (format->sym_count == 0) result = NULL; else { assert(index < format->sym_count); result = format->symbols[index]; assert(result != NULL); g_object_ref(G_OBJECT(result)); } return result; } /****************************************************************************** * * * Paramètres : format = informations chargées à compléter. * * symbol = symbole à ajouter à la liste. * * * * Description : Ajoute un symbole à la collection du format binaire. * * * * Retour : true si le symbole était bien localisé et a été inséré. * * * * Remarques : - * * * ******************************************************************************/ bool g_program_format_add_symbol(GProgramFormat *format, GBinSymbol *symbol) { bool result; /* Statut d'ajout à retourner */ #ifndef NDEBUG const mrange_t *range; /* Couverture du symbole */ const vmpa2t *addr; /* Emplacement du symbole */ #endif size_t index; /* Indice du point d'insertion */ /** * Pour que les fonctions de recherche basées sur _g_program_format_find_symbol() * fassent bien leur office, il faut que les symboles soient triés. * * Cependant, les localisations à satisfaire lors d'une recherche recontrent * un problème si les positions physiques ne sont pas renseignées. En effet * les adresses virtuelles en sont potentiellement décorrélées (c'est le cas * avec le format ELF par exemple, où les zones en mémoire ne suivent pas le * même ordre que les segments du binaire). * * Comme les comparaisons entre localisations se réalisent sur les éléments * renseignés communs, à commencer par la position physique si c'est possible, * une localisation s'appuyant uniquement sur une adresse virtuelle va être * analysée suivant une liste non triée d'adresses virtuelles. * * On corrige donc le tir si besoin est en forçant la comparaison via les * positions physiques. */ #ifndef NDEBUG range = g_binary_symbol_get_range(symbol); addr = get_mrange_addr(range); assert(has_phys_addr(addr) || g_binary_symbol_get_status(symbol) == SSS_DYNAMIC); #endif g_program_format_lock_unlock_symbols_wr(format, true); /** * Avec tous les traitements parallèles, il est possible que plusieurs chemins d'exécution * amènent à la création d'un même symbole. * * Plutôt que de verrouiller la liste des symboles en amont (et donc assez longtemps) * pour faire une vérification avant construction puis ajout, on préfère limiter * l'état figé à cette seule fonction, quitte à annuler le travail fourni pour la * construction du symbole dans les cas peu fréquents où le symbole était déjà en place. */ result = bsearch_index(&symbol, format->symbols, format->sym_count, sizeof(GBinSymbol *), (__compar_fn_t)g_binary_symbol_cmp, &index); if (!result) { format->symbols = _qinsert(format->symbols, &format->sym_count, sizeof(GBinSymbol *), &symbol, index); format->sym_stamp++; result = true; } else g_object_unref(G_OBJECT(symbol)); g_program_format_lock_unlock_symbols_wr(format, false); if (result) g_signal_emit_by_name(format, "symbol-added", symbol); return result; } /****************************************************************************** * * * Paramètres : format = informations chargées à compléter. * * symbols = ensemble de symboles à ajouter à la liste. * * count = taille de cet ensemble. * * * * Description : Ajoute plusieurs symboles à la collection du format binaire. * * * * Retour : true si les symboles dûment localisés ont été insérés. * * * * Remarques : - * * * ******************************************************************************/ bool g_program_format_add_symbols(GProgramFormat *format, GBinSymbol **symbols, size_t count) { bool result; /* Statut d'ajout à retourner */ #ifndef NDEBUG phys_t last; /* Dernière position rencontrée*/ #endif size_t i; /* Boucle de parcours */ #ifndef NDEBUG const mrange_t *range; /* Couverture du symbole */ const vmpa2t *addr; /* Emplacement du symbole */ #endif size_t index; /* Indice du point d'insertion */ /** * Pour que les fonctions de recherche basées sur _g_program_format_find_symbol() * fassent bien leur office, il faut que les symboles soient triés. * * Cependant, les localisations à satisfaire lors d'une recherche recontrent * un problème si les positions physiques ne sont pas renseignées. En effet * les adresses virtuelles en sont potentiellement décorrélées (c'est le cas * avec le format ELF par exemple, où les zones en mémoire ne suivent pas le * même ordre que les segments du binaire). * * Comme les comparaisons entre localisations se réalisent sur les éléments * renseignés communs, à commencer par la position physique si c'est possible, * une localisation s'appuyant uniquement sur une adresse virtuelle va être * analysée suivant une liste non triée d'adresses virtuelles. * * On corrige donc le tir si besoin est en forçant la comparaison via les * positions physiques. */ #ifndef NDEBUG last = VMPA_NO_PHYSICAL; for (i = 0; i < count; i++) { range = g_binary_symbol_get_range(symbols[i]); addr = get_mrange_addr(range); assert(has_phys_addr(addr) || g_binary_symbol_get_status(symbols[i]) == SSS_DYNAMIC); if (has_phys_addr(addr)) { assert(last == VMPA_NO_PHYSICAL || last <= get_phy_addr(addr)); last = get_phy_addr(addr); } } #endif g_program_format_lock_unlock_symbols_wr(format, true); /** * Avec tous les traitements parallèles, il est possible que plusieurs chemins d'exécution * amènent à la création d'un même symbole. * * Plutôt que de verrouiller la liste des symboles en amont (et donc assez longtemps) * pour faire une vérification avant construction puis ajout, on préfère limiter * l'état figé à cette seule fonction, quitte à annuler le travail fourni pour la * construction du symbole dans les cas peu fréquents où le symbole était déjà en place. */ result = bsearch_index(&symbols[0], format->symbols, format->sym_count, sizeof(GBinSymbol *), (__compar_fn_t)g_binary_symbol_cmp, &index); if (!result) { for (i = 0; i < count; i++) g_object_ref(G_OBJECT(symbols[i])); format->symbols = _qinsert_batch(format->symbols, &format->sym_count, sizeof(GBinSymbol *), symbols, count, index); format->sym_stamp++; result = true; } g_program_format_lock_unlock_symbols_wr(format, false); if (result) for (i = 0; i < count; i++) g_signal_emit_by_name(format, "symbol-added", symbols[i]); return result; } /****************************************************************************** * * * Paramètres : format = informations chargées à compléter. * * index = indice du symbole à retirer de la liste. * * * * Description : Retire un symbole de la collection du format binaire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void _g_program_format_remove_symbol(GProgramFormat *format, size_t index) { assert(g_atomic_int_get(&format->sym_locked) == 1); assert(index < format->sym_count); g_object_unref(G_OBJECT(format->symbols[index])); if ((index + 1) < format->sym_count) memmove(&format->symbols[index], &format->symbols[index + 1], (format->sym_count - index - 1) * sizeof(GBinSymbol *)); format->symbols = realloc(format->symbols, --format->sym_count * sizeof(GBinSymbol *)); } /****************************************************************************** * * * Paramètres : format = informations chargées à compléter. * * symbol = symbole à retirer de la liste. * * * * Description : Retire un symbole de la collection du format binaire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_program_format_remove_symbol(GProgramFormat *format, GBinSymbol *symbol) { bool found; /* Jeton de présence */ size_t index; /* Indice du point de retrait */ g_object_ref(G_OBJECT(symbol)); g_program_format_lock_unlock_symbols_wr(format, true); found = bsearch_index(&symbol, format->symbols, format->sym_count, sizeof(GBinSymbol *), (__compar_fn_t)g_binary_symbol_cmp, &index); if (found) _g_program_format_remove_symbol(format, index); g_program_format_lock_unlock_symbols_wr(format, false); if (found) g_signal_emit_by_name(format, "symbol-removed", symbol); g_object_unref(G_OBJECT(symbol)); } /****************************************************************************** * * * Paramètres : format = informations chargées à consulter. * * label = étiquette à retrouver lors des recherches. * * symbol = éventuel symbole trouvé à déréfenrencer. [OUT] * * * * Description : Recherche le symbole correspondant à une étiquette. * * * * Retour : true si l'opération a été un succès, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool g_program_format_find_symbol_by_label(GProgramFormat *format, const char *label, GBinSymbol **symbol) { bool result; /* Bilan à retourner */ size_t i; /* Boucle de parcours */ char *cur_lbl; /* Etiquette courante */ result = false; g_program_format_lock_symbols_rd(format); for (i = 0; i < format->sym_count && !result; i++) { cur_lbl = g_binary_symbol_get_label(format->symbols[i]); if (cur_lbl == NULL) continue; if (strcmp(label, cur_lbl) == 0) { *symbol = format->symbols[i]; g_object_ref(G_OBJECT(*symbol)); result = true; } free(cur_lbl); } g_program_format_unlock_symbols_rd(format); return result; } /****************************************************************************** * * * Paramètres : format = informations chargées à consulter. * * addr = adresse à cibler lors des recherches. * * fn = méthode de comparaison des symboles. * * index = indice de l'éventuel symbole trouvé ou NULL. [OUT] * * symbol = éventuel symbole trouvé à déréfenrencer. [OUT] * * * * Description : Recherche le symbole associé à une adresse. * * * * Retour : true si l'opération a été un succès, false sinon. * * * * Remarques : - * * * ******************************************************************************/ static bool _g_program_format_find_symbol(const GProgramFormat *format, const vmpa2t *addr, __compar_fn_t fn, size_t *index, GBinSymbol **symbol) { /** * Pour ce qui est des justifications quant à la vérification suivante, * se référer aux commentaires placés dans g_program_format_add_symbol(). */ assert(has_phys_addr(addr)); return __g_program_format_find_symbol(format, addr, fn, index, symbol); } /****************************************************************************** * * * Paramètres : format = informations chargées à consulter. * * key = clef fournie pour distinguer les éléments. * * fn = méthode de comparaison des symboles. * * index = indice de l'éventuel symbole trouvé ou NULL. [OUT] * * symbol = éventuel symbole trouvé à déréfenrencer. [OUT] * * * * Description : Recherche un symbole particulier. * * * * Retour : true si l'opération a été un succès, false sinon. * * * * Remarques : - * * * ******************************************************************************/ static bool __g_program_format_find_symbol(const GProgramFormat *format, const void *key, __compar_fn_t fn, size_t *index, GBinSymbol **symbol) { bool result; /* Bilan à retourner */ void *found; /* Résultat de recherches */ assert(g_atomic_int_get(&format->sym_locked) > 0); found = bsearch(key, format->symbols, format->sym_count, sizeof(GBinSymbol *), fn); if (found != NULL) { if (index != NULL) *index = (GBinSymbol **)found - format->symbols; if (symbol != NULL) { *symbol = *(GBinSymbol **)found; g_object_ref(G_OBJECT(*symbol)); } result = true; } else { if (symbol != NULL) *symbol = NULL; result = false; } return result; } /****************************************************************************** * * * Paramètres : format = informations chargées à consulter. * * addr = adresse à cibler lors des recherches. * * index = indice de l'éventuel symbole trouvé. [OUT] * * * * Description : Recherche l'indice du symbole correspondant à une adresse. * * * * Retour : true si l'opération a été un succès, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool g_program_format_find_symbol_index_at(GProgramFormat *format, const vmpa2t *addr, size_t *index) { bool result; /* Bilan à retourner */ int find_symbol(const vmpa2t *addr, const GBinSymbol **sym) { const mrange_t *range; /* Espace mémoire parcouru */ range = g_binary_symbol_get_range(*sym); return cmp_vmpa(addr, get_mrange_addr(range)); } g_program_format_lock_symbols_rd(format); result = _g_program_format_find_symbol(format, addr, (__compar_fn_t)find_symbol, index, NULL); g_program_format_unlock_symbols_rd(format); return result; } /****************************************************************************** * * * Paramètres : format = informations chargées à consulter. * * addr = adresse à cibler lors des recherches. * * symbol = éventuel symbole trouvé à déréfenrencer. [OUT] * * * * Description : Recherche le symbole correspondant à une adresse. * * * * Retour : true si l'opération a été un succès, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool g_program_format_find_symbol_at(GProgramFormat *format, const vmpa2t *addr, GBinSymbol **symbol) { bool result; /* Bilan à retourner */ int find_symbol(const vmpa2t *addr, const GBinSymbol **sym) { const mrange_t *range; /* Espace mémoire parcouru */ range = g_binary_symbol_get_range(*sym); return cmp_vmpa(addr, get_mrange_addr(range)); } g_program_format_lock_symbols_rd(format); result = _g_program_format_find_symbol(format, addr, (__compar_fn_t)find_symbol, NULL, symbol); g_program_format_unlock_symbols_rd(format); return result; } /****************************************************************************** * * * Paramètres : format = informations chargées à consulter. * * addr = adresse à cibler lors des recherches. * * symbol = éventuel symbole trouvé à déréfenrencer. [OUT] * * * * Description : Recherche le symbole contenant une adresse. * * * * Retour : true si l'opération a été un succès, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool g_program_format_find_symbol_for(GProgramFormat *format, const vmpa2t *addr, GBinSymbol **symbol) { bool result; /* Bilan à retourner */ int find_symbol(const vmpa2t *addr, const GBinSymbol **sym) { const mrange_t *range; /* Espace mémoire parcouru */ range = g_binary_symbol_get_range(*sym); return cmp_mrange_with_vmpa(range, addr); } g_program_format_lock_symbols_rd(format); result = _g_program_format_find_symbol(format, addr, (__compar_fn_t)find_symbol, NULL, symbol); g_program_format_unlock_symbols_rd(format); return result; } /****************************************************************************** * * * Paramètres : format = informations chargées à consulter. * * addr = adresse à cibler lors des recherches. * * symbol = éventuel symbole trouvé à déréfenrencer. [OUT] * * * * Description : Recherche le symbole suivant celui lié à une adresse. * * * * Retour : true si l'opération a été un succès, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool g_program_format_find_next_symbol_at(GProgramFormat *format, const vmpa2t *addr, GBinSymbol **symbol) { bool result; /* Bilan à retourner */ size_t index; /* Indice à considérer */ int find_symbol(const vmpa2t *addr, const GBinSymbol **sym) { const mrange_t *range; /* Espace mémoire parcouru */ range = g_binary_symbol_get_range(*sym); return cmp_mrange_with_vmpa(range, addr); } g_program_format_lock_symbols_rd(format); result = _g_program_format_find_symbol(format, addr, (__compar_fn_t)find_symbol, &index, NULL); if (result && (index + 1) < format->sym_count) { *symbol = format->symbols[index + 1]; g_object_ref(G_OBJECT(*symbol)); } else { *symbol = NULL; result = false; } g_program_format_unlock_symbols_rd(format); return result; } /****************************************************************************** * * * Paramètres : format = informations chargées à consulter. * * range = zone à cibler lors des recherches. * * index = indice de l'éventuel symbole trouvé. [OUT] * * * * Description : Recherche le premier symbole inclus dans une zone mémoire. * * * * Retour : true si l'opération a été un succès, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool g_program_format_find_first_symbol_inside(GProgramFormat *format, const mrange_t *range, size_t *index) { bool result; /* Bilan à retourner */ const GBinSymbol *prev; /* Symbole précédent */ const mrange_t *srange; /* Espace mémoire associé */ int ret; /* Bilan de comparaison */ int find_symbol(const mrange_t *ref_range, const GBinSymbol **sym) { const mrange_t *sym_range; /* Espace mémoire parcouru */ int ret; sym_range = g_binary_symbol_get_range(*sym); ret = cmp_mrange_with_vmpa(ref_range, get_mrange_addr(sym_range)); ret *= -1; return ret; } g_rw_lock_reader_lock(&format->syms_lock); result = __g_program_format_find_symbol(format, range, (__compar_fn_t)find_symbol, index, NULL); if (result) while (*index > 0) { prev = format->symbols[*index - 1]; srange = g_binary_symbol_get_range(prev); ret = cmp_mrange_with_vmpa(range, get_mrange_addr(srange)); assert(ret <= 0); if (ret < 0) break; else (*index)--; } g_rw_lock_reader_unlock(&format->syms_lock); return result; } /****************************************************************************** * * * Paramètres : format = informations chargées à consulter. * * addr = adresse à cibler lors des recherches. * * strict = indication de tolérance acceptée. * * symbol = éventuel symbole trouvé à déréfenrencer. [OUT] * * diff = décalage entre l'adresse et le symbole. [OUT] * * * * Description : Recherche le symbole correspondant à une adresse. * * * * Retour : true si l'opération a été un succès, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool g_program_format_resolve_symbol(GProgramFormat *format, const vmpa2t *addr, bool strict, GBinSymbol **symbol, phys_t *diff) { bool result; /* Bilan à retourner */ const mrange_t *range; /* Espace mémoire parcouru */ if (strict) result = g_program_format_find_symbol_at(format, addr, symbol); else result = g_program_format_find_symbol_for(format, addr, symbol); if (result) { range = g_binary_symbol_get_range(*symbol); *diff = compute_vmpa_diff(get_mrange_addr(range), addr); assert(!strict || *diff == 0); } else *diff = 0; return result; } /* ---------------------------------------------------------------------------------- */ /* CONSERVATION DES SOUCIS DURANT LE CHARGEMENT */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : format = architecture à manipuler. * * state = nouvel état de l'accès aux erreurs relevées. * * * * Description : Protège ou lève la protection de l'accès aux erreurs. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_program_format_lock_unlock_errors(GProgramFormat *format, bool state) { if (state) { g_mutex_lock(&format->error_mutex); #ifndef NDEBUG g_atomic_int_set(&format->error_locked, 1); #endif } else { #ifndef NDEBUG g_atomic_int_set(&format->error_locked, 0); #endif g_mutex_unlock(&format->error_mutex); } } /****************************************************************************** * * * Paramètres : format = architecture concernée par la procédure. * * index = indice du problème visé. * * type = type d'erreur retrouvée. * * addr = localisation associée. * * desc = éventuelle description humaine de description. * * * * Description : Etend la liste des soucis détectés avec de nouvelles infos. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_program_format_add_error(GProgramFormat *format, BinaryFormatError type, const vmpa2t *addr, const char *desc) { fmt_error *error; /* Raccourci de confort */ g_program_format_lock_errors(format); format->errors = realloc(format->errors, ++format->error_count * sizeof(fmt_error)); error = &format->errors[format->error_count - 1]; error->type = type; copy_vmpa(&error->addr, addr); if (desc != NULL) error->desc = strdup(desc); else error->desc = NULL; g_program_format_unlock_errors(format); } /****************************************************************************** * * * Paramètres : format = architecture à consulter durant la procédure. * * * * Description : Indique le nombre d'erreurs relevées au niveau assembleur. * * * * Retour : Nombre d'erreurs en stock. * * * * Remarques : - * * * ******************************************************************************/ size_t g_program_format_count_errors(GProgramFormat *format) { size_t result; /* Quantité à retourner */ assert(g_atomic_int_get(&format->error_locked) == 1); result = format->error_count; return result; } /****************************************************************************** * * * Paramètres : format = architecture concernée par la procédure. * * index = indice du problème visé. * * type = type d'erreur retrouvée. [OUT] * * addr = localisation associée. [OUT] * * desc = éventuelle description humaine de description. [OUT]* * * * Description : Fournit les éléments concernant un soucis détecté. * * * * Retour : Validité des informations renseignées. * * * * Remarques : - * * * ******************************************************************************/ bool g_program_format_get_error(GProgramFormat *format, size_t index, BinaryFormatError *type, vmpa2t *addr, char **desc) { bool result; /* Bilan à retourner */ fmt_error *error; /* Raccourci de confort */ assert(g_atomic_int_get(&format->error_locked) == 1); result = (index < format->error_count); assert(result); if (result) { error = &format->errors[index]; *type = error->type; copy_vmpa(addr, &error->addr); if (error->desc != NULL) *desc = strdup(error->desc); else *desc = NULL; } return result; } /****************************************************************************** * * * Paramètres : format = format de binaire concerné par la procédure. * * pbuf = zone tampon à vider. * * * * Description : Charge les erreurs de chargement depuis une mémoire tampon. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ static bool g_program_format_load_errors(GProgramFormat *format, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ uleb128_t value; /* Valeur ULEB128 à charger */ size_t i; /* Boucle de parcours */ fmt_error *error; /* Raccourci de confort */ rle_string str; /* Chaîne à charger */ g_program_format_lock_errors(format); result = unpack_uleb128(&value, pbuf); if (!result) goto exit; format->error_count = value; format->errors = calloc(format->error_count, sizeof(fmt_error)); for (i = 0; i < format->error_count && result; i++) { error = &format->errors[i]; result = unpack_uleb128(&value, pbuf); if (!result) break; error->type = value; result = unpack_vmpa(&error->addr, pbuf); if (!result) break; setup_empty_rle_string(&str); result = unpack_rle_string(&str, pbuf); if (!result) break; if (get_rle_string(&str) != NULL) error->desc = strdup(get_rle_string(&str)); exit_rle_string(&str); } exit: g_program_format_unlock_errors(format); return result; } /****************************************************************************** * * * Paramètres : format = format de binaire concerné par la procédure. * * pbuf = zone tampon à remplir. * * * * Description : Sauvegarde les erreurs de chargement dans une mémoire tampon.* * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ static bool g_program_format_store_errors(GProgramFormat *format, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ size_t i; /* Boucle de parcours */ fmt_error *error; /* Raccourci de confort */ rle_string str; /* Chaîne à conserver */ g_program_format_lock_errors(format); result = pack_uleb128((uleb128_t []){ format->error_count }, pbuf); for (i = 0; i < format->error_count && result; i++) { error = &format->errors[i]; result = pack_uleb128((uleb128_t []){ error->type }, pbuf); if (!result) break; result = pack_vmpa(&error->addr, pbuf); if (!result) break; init_static_rle_string(&str, error->desc); result = pack_rle_string(&str, pbuf); exit_rle_string(&str); } g_program_format_unlock_errors(format); return result; } /* ---------------------------------------------------------------------------------- */ /* IMPLEMENTATION DES FONCTIONS DE CLASSE */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : operand = élément GLib à constuire. * * storage = conservateur de données à manipuler ou NULL. * * pbuf = zone tampon à lire. * * * * Description : Charge un format depuis une mémoire tampon. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ static bool g_program_format_load(GProgramFormat *format, GObjectStorage *storage, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ fmt_extra_data_t *extra; /* Données insérées à consulter*/ uleb128_t value; /* Valeur ULEB128 à charger */ rle_string str; /* Chaîne à charger */ extra = GET_BIN_FORMAT_EXTRA(format); LOCK_GOBJECT_EXTRA(extra); result = unpack_uleb128(&value, pbuf); if (result) extra->flags = value; UNLOCK_GOBJECT_EXTRA(extra); if (result) result = g_program_format_load_start_points(format, pbuf); if (result) { setup_empty_rle_string(&str); result = unpack_rle_string(&str, pbuf); if (result) result = (get_rle_string(&str) != NULL); if (result) format->demangler = get_compiler_demangler_for_key(get_rle_string(&str)); if (result) result = (format->demangler != NULL); exit_rle_string(&str); } if (result) result = g_program_format_load_errors(format, pbuf); return result; } /****************************************************************************** * * * Paramètres : operand = élément GLib à consulter. * * storage = conservateur de données à manipuler ou NULL. * * pbuf = zone tampon à remplir. * * * * Description : Sauvegarde un format dans une mémoire tampon. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ static bool g_program_format_store(GProgramFormat *format, GObjectStorage *storage, packed_buffer_t *pbuf) { bool result; /* Bilan à retourner */ fmt_extra_data_t *extra; /* Données insérées à consulter*/ char *key; /* Désignation du décodeur */ rle_string str; /* Chaîne à conserver */ extra = GET_BIN_FORMAT_EXTRA(format); LOCK_GOBJECT_EXTRA(extra); result = pack_uleb128((uleb128_t []){ extra->flags }, pbuf); UNLOCK_GOBJECT_EXTRA(extra); if (result) result = g_program_format_store_start_points(format, pbuf); if (result) { key = g_compiler_demangler_get_key(format->demangler); init_dynamic_rle_string(&str, key); result = pack_rle_string(&str, pbuf); exit_rle_string(&str); } if (result) result = g_program_format_store_errors(format, pbuf); return result; } #endif