/* Chrysalide - Outil d'analyse de fichiers binaires * symbols.c - panneau d'affichage des symboles * * Copyright (C) 2012-2017 Cyrille Bagard * * This file is part of Chrysalide. * * Chrysalide is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * Chrysalide is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "symbols.h" #include #include #include #include #include #include #include #include "panel-int.h" #include "updating-int.h" #include "../agroup.h" #include "../core/global.h" #include "../../common/extstr.h" #include "../../core/paths.h" #include "../../core/queue.h" #include "../../format/format.h" #include "../../format/symiter.h" #include "../../gtkext/easygtk.h" #include "../../gtkext/gtkdisplaypanel.h" #include "../../gtkext/tmgt.h" #include "../../mangling/demangler.h" /* -------------------------- PARTIE PRINCIPALE DU PANNEAU -------------------------- */ /* Panneau d'affichage des symboles (instance) */ struct _GSymbolsPanel { GPanelItem parent; /* A laisser en premier */ GLoadedBinary *binary; /* Binaire à prendre en compte */ const char *sep; /* Délimitateur à utiliser */ size_t count; /* Quantité de symboles utiles */ }; /* Panneau d'affichage des symboles (classe) */ struct _GSymbolsPanelClass { GPanelItemClass parent; /* A laisser en premier */ cairo_surface_t *routine_img; /* Image pour les routines */ cairo_surface_t *object_img; /* Image pour les objets */ cairo_surface_t *package_img; /* Image pour les paquets */ cairo_surface_t *class_img; /* Image pour les classes */ }; /* Colonnes de la liste des symboles */ typedef enum _SymbolsColumn { SBC_SYMBOL, /* Symbole représenté */ SBC_PICTURE, /* Image de représentation */ SBC_NAME, /* Désignation humaine */ SBC_ORIGINAL, /* Version brute d'origine */ SBC_ADDRESS, /* Adresse mémoire du symbole */ SBC_SECTION, /* Section d'appartenance */ SBC_EXPAND, /* Affichage des classes */ SBC_MATCHED, /* Correspondance établie ? */ SBC_MATCH_POINTS, /* Nombre de demandeurs */ SBC_COUNT /* Nombre de colonnes */ } SymbolsColumn; /* Données utiles à la mise à jour */ typedef struct _symbols_update_data symbols_update_data; /* Initialise la classe des panneaux d'affichage des symboles. */ static void g_symbols_panel_class_init(GSymbolsPanelClass *); /* Initialise une instance de panneau d'affichage des symboles. */ static void g_symbols_panel_init(GSymbolsPanel *); /* Procède à l'initialisation de l'interface de mise à jour. */ static void g_symbols_panel_updatable_interface_init(GUpdatablePanelInterface *); /* Supprime toutes les références externes. */ static void g_symbols_panel_dispose(GSymbolsPanel *); /* Procède à la libération totale de la mémoire. */ static void g_symbols_panel_finalize(GSymbolsPanel *); /* Bascule d'affichage des symboles en liste. */ static void on_symbols_list_display_toggle(GtkToggleToolButton *, GSymbolsPanel *); /* Bascule l'affichage des symboles en arborescence. */ static void on_symbols_tree_display_toggle(GtkToggleToolButton *, GSymbolsPanel *); /* Réagit au changement de sélection des symboles. */ static void on_symbols_selection_change(GtkTreeSelection *, gpointer); /* Réagit à un changement d'affichage principal de contenu. */ static void change_symbols_panel_current_content(GSymbolsPanel *, GLoadedContent *, GLoadedContent *); /* Réagit à un changement d'affichage principal de contenu. */ static void reload_symbols_panel_content(const GSymbolsPanel *, GtkStatusStack *, activity_id_t, symbols_update_data *); /* ------------------------- AFFICHAGE A L'AIDE D'UNE LISTE ------------------------- */ /* Réagit à un changement d'affichage principal de contenu. */ static void reload_symbols_for_new_list_view(const GSymbolsPanel *, GtkStatusStack *, activity_id_t, symbols_update_data *); /* Met en surbrillance les éléments recherchés dans les noms. */ static void update_symbol_name_in_list_view(GtkTreeStore *, GtkTreeIter *, const regmatch_t *); /* -------------------------- AFFICHAGE SOUS FORME D'ARBRE -------------------------- */ /* S'assure qu'un noeud donné existe bien. */ static GtkTreeIter ensure_symbol_node_exist(const GSymbolsPanel *, GtkTreeIter *, const char *, const regmatch_t *, size_t); /* Détermine le point d'insertion parent d'une routine. */ static bool find_parent_for_symbol(const GSymbolsPanel *, const GBinSymbol *, GtkTreeIter *, const regmatch_t *, size_t *); /* Réagit à un changement d'affichage principal de contenu. */ static void reload_symbols_for_new_tree_view(const GSymbolsPanel *, GtkStatusStack *, activity_id_t, symbols_update_data *); /* Réagit à une nouvelle demande de réorganisation. */ static void reorganize_symbols_tree_view(GtkToolButton *, const GSymbolsPanel *); /* Fait en sorte que toutes les classes soient affichées. */ static gboolean show_all_classes_in_tree_view(GtkTreeModel *, GtkTreePath *, GtkTreeIter *, GtkTreeView *); /* Actualise une partie d'un nom de symbole éclaté en noeuds. */ static GtkTreeIter update_symbol_partial_name_in_tree_view(GtkTreeStore *, GtkTreeIter *, const char *, const regmatch_t *, size_t); /* Met en surbrillance les éléments recherchés dans les noms. */ static void update_symbol_name_in_tree_view(const GSymbolsPanel *, GtkTreeStore *, const GBinSymbol *, const regmatch_t *); /* ------------------------- FILTRAGE DES SYMBOLES PRESENTS ------------------------- */ /* Démarre l'actualisation du filtrage des symboles. */ static void on_symbols_filter_changed(GtkSearchEntry *, GSymbolsPanel *); /* Exécute un nouveau filtrage des symboles affichés. */ static void do_filtering_on_symbols(const GSymbolsPanel *, GtkStatusStack *, activity_id_t, symbols_update_data *); /* ---------------------- MECANISMES DE MISE A JOUR DE PANNEAU ---------------------- */ /* Données utiles à la mise à jour */ struct _symbols_update_data { size_t count; /* Qté d'inscriptions réalisées*/ regex_t *filter; /* Filtre appliqué ou NULL */ char **expanded; /* Chemins des noeuds ouverts */ size_t ecount; /* Nombre de ces chemins */ size_t eallocated; /* Espace alloué effectivement */ }; #define EXPAND_ALLOC_RANGE 10 /* Détermine si un nom de symbole doit être filtré ou non. */ static bool is_symbol_matching(const symbols_update_data *, const GBinSymbol *, regmatch_t *); /* Prépare une opération de mise à jour de panneau. */ static const char *g_symbols_panel_setup(const GSymbolsPanel *, unsigned int, size_t *, symbols_update_data **); /* Bascule l'affichage d'un panneau avant mise à jour. */ static void g_symbols_panel_introduce(const GSymbolsPanel *, unsigned int, symbols_update_data *); /* Réalise une opération de mise à jour de panneau. */ static void g_symbols_panel_process(const GSymbolsPanel *, unsigned int, GtkStatusStack *, activity_id_t, symbols_update_data *); /* Bascule l'affichage d'un panneau après mise à jour. */ static void g_symbols_panel_conclude(GSymbolsPanel *, unsigned int, symbols_update_data *); /* Supprime les données dynamiques utilisées à la mise à jour. */ static void g_symbols_panel_clean_data(GUpdatablePanel *, unsigned int, symbols_update_data *); /* ---------------------------------------------------------------------------------- */ /* PARTIE PRINCIPALE DU PANNEAU */ /* ---------------------------------------------------------------------------------- */ /* Indique le type définit pour un panneau d'affichage des symboles. */ G_DEFINE_TYPE_WITH_CODE(GSymbolsPanel, g_symbols_panel, G_TYPE_PANEL_ITEM, G_IMPLEMENT_INTERFACE(G_TYPE_UPDATABLE_PANEL, g_symbols_panel_updatable_interface_init)); /****************************************************************************** * * * Paramètres : klass = classe à initialiser. * * * * Description : Initialise la classe des panneaux d'affichage des symboles. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_symbols_panel_class_init(GSymbolsPanelClass *klass) { GObjectClass *object; /* Autre version de la classe */ GEditorItemClass *editem; /* Encore une autre vision... */ GPanelItemClass *panel; /* Version parente de la classe*/ gchar *filename; /* Chemin d'accès à utiliser */ object = G_OBJECT_CLASS(klass); object->dispose = (GObjectFinalizeFunc/* ! */)g_symbols_panel_dispose; object->finalize = (GObjectFinalizeFunc)g_symbols_panel_finalize; editem = G_EDITOR_ITEM_CLASS(klass); editem->change_content = (change_item_content_fc)change_symbols_panel_current_content; panel = G_PANEL_ITEM_CLASS(klass); panel->unique = true; panel->bindings = "F3"; panel->gid = setup_tiny_global_work_group(1); filename = find_pixmap_file("symbol_routine_classic.png"); assert(filename != NULL); klass->routine_img = cairo_image_surface_create_from_png(filename); g_free(filename); filename = find_pixmap_file("symbol_object_classic.png"); assert(filename != NULL); klass->object_img = cairo_image_surface_create_from_png(filename); g_free(filename); filename = find_pixmap_file("symbol_package.png"); assert(filename != NULL); klass->package_img = cairo_image_surface_create_from_png(filename); g_free(filename); filename = find_pixmap_file("symbol_class_classic.png"); assert(filename != NULL); klass->class_img = cairo_image_surface_create_from_png(filename); g_free(filename); } /****************************************************************************** * * * Paramètres : panel = instance à initialiser. * * * * Description : Initialise une instance de panneau d'affichage des symboles. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_symbols_panel_init(GSymbolsPanel *panel) { GEditorItem *base; /* Version basique d'instance */ GPanelItem *pitem; /* Version parente du panneau */ GtkBuilder *builder; /* Constructeur utilisé */ GtkTreeModelFilter *filter; /* Filtre pour l'arborescence */ GtkTreeView *treeview; /* Affichage de la liste */ GtkCellRenderer *renderer; /* Moteur de rendu de colonne */ GtkTreeViewColumn *column; /* Colonne de la liste */ /* Eléments de base */ base = G_EDITOR_ITEM(panel); base->name = PANEL_SYMBOLS_ID; pitem = G_PANEL_ITEM(panel); pitem->personality = PIP_SINGLETON; pitem->lname = _("Binary symbols"); pitem->dock_at_startup = true; pitem->path = strdup("eN"); /* Représentation graphique */ builder = g_panel_item_build(pitem, "symbols"); filter = GTK_TREE_MODEL_FILTER(gtk_builder_get_object(builder, "filter")); gtk_tree_model_filter_set_visible_column(filter, SBC_MATCHED); /* Cellules d'affichage */ treeview = GTK_TREE_VIEW(gtk_builder_get_object(builder, "treeview")); column = gtk_tree_view_column_new(); renderer = gtk_cell_renderer_pixbuf_new(); gtk_tree_view_column_pack_start(column, renderer, FALSE); gtk_tree_view_column_add_attribute(column, renderer, "surface", SBC_PICTURE); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_column_pack_start(column, renderer, TRUE); gtk_tree_view_column_add_attribute(column, renderer, "markup", SBC_NAME); gtk_tree_view_column_set_sort_column_id(column, SBC_NAME); gtk_tree_view_column_set_title(column, _("Name")); gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes(_("Address"), renderer, "text", SBC_ADDRESS, NULL); gtk_tree_view_column_set_sort_column_id(column, SBC_ADDRESS); gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes(_("Section"), renderer, "text", SBC_SECTION, NULL); gtk_tree_view_column_set_sort_column_id(column, SBC_SECTION); gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); /* Connexion des signaux */ gtk_builder_add_callback_symbols(builder, "on_symbols_list_display_toggle", G_CALLBACK(on_symbols_list_display_toggle), "on_symbols_tree_display_toggle", G_CALLBACK(on_symbols_tree_display_toggle), "reorganize_symbols_tree_view", G_CALLBACK(reorganize_symbols_tree_view), "on_symbols_filter_changed", G_CALLBACK(on_symbols_filter_changed), "on_symbols_selection_change", G_CALLBACK(on_symbols_selection_change), "track_focus_change_in_text_area", G_CALLBACK(track_focus_change_in_text_area), NULL); gtk_builder_connect_signals(builder, panel); } /****************************************************************************** * * * Paramètres : iface = interface GLib à initialiser. * * * * Description : Procède à l'initialisation de l'interface de mise à jour. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_symbols_panel_updatable_interface_init(GUpdatablePanelInterface *iface) { iface->setup = (setup_updatable_cb)g_symbols_panel_setup; iface->get_group = (get_updatable_group_cb)g_panel_item_get_group; iface->introduce = (introduce_updatable_cb)g_symbols_panel_introduce; iface->process = (process_updatable_cb)g_symbols_panel_process; iface->conclude = (conclude_updatable_cb)g_symbols_panel_conclude; iface->clean = (clean_updatable_data_cb)g_symbols_panel_clean_data; } /****************************************************************************** * * * Paramètres : panel = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_symbols_panel_dispose(GSymbolsPanel *panel) { if (panel->binary != NULL) g_object_unref(G_OBJECT(panel->binary)); G_OBJECT_CLASS(g_symbols_panel_parent_class)->dispose(G_OBJECT(panel)); } /****************************************************************************** * * * Paramètres : panel = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_symbols_panel_finalize(GSymbolsPanel *panel) { G_OBJECT_CLASS(g_symbols_panel_parent_class)->finalize(G_OBJECT(panel)); } /****************************************************************************** * * * Paramètres : - * * * * Description : Crée un panneau d'affichage des symboles. * * * * Retour : Adresse de la structure mise en place. * * * * Remarques : - * * * ******************************************************************************/ GPanelItem *g_symbols_panel_new(void) { GPanelItem *result; /* Structure à retourner */ result = g_object_new(G_TYPE_SYMBOLS_PANEL, NULL); return result; } /****************************************************************************** * * * Paramètres : button = bouton de la barre activé. * * panel = structure contenant les informations maîtresses. * * * * Description : Bascule d'affichage des symboles en liste. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void on_symbols_list_display_toggle(GtkToggleToolButton *button, GSymbolsPanel *panel) { GtkBuilder *builder; /* Constructeur utilisé */ GtkWidget *other; /* Autre bouton de la barre */ GLoadedContent *content; /* Autre version du binaire */ if (gtk_toggle_tool_button_get_active(button)) { /* Accès aux boutons complémentaires */ builder = G_PANEL_ITEM(panel)->builder; other = GTK_WIDGET(gtk_builder_get_object(builder, "collapse")); gtk_widget_set_sensitive(other, FALSE); other = GTK_WIDGET(gtk_builder_get_object(builder, "expand")); gtk_widget_set_sensitive(other, FALSE); other = GTK_WIDGET(gtk_builder_get_object(builder, "classes")); gtk_widget_set_sensitive(other, FALSE); /* Actualisation de l'affichage */ if (panel->binary != NULL) { content = G_LOADED_CONTENT(panel->binary); g_object_ref(G_OBJECT(content)); change_symbols_panel_current_content(panel, content, content); g_object_unref(G_OBJECT(content)); } } } /****************************************************************************** * * * Paramètres : button = bouton de la barre activé. * * panel = structure contenant les informations maîtresses. * * * * Description : Bascule l'affichage des symboles en arborescence. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void on_symbols_tree_display_toggle(GtkToggleToolButton *button, GSymbolsPanel *panel) { GtkBuilder *builder; /* Constructeur utilisé */ GtkWidget *other; /* Autre bouton de la barre */ GLoadedContent *content; /* Autre version du binaire */ if (gtk_toggle_tool_button_get_active(button)) { /* Accès aux boutons complémentaires */ builder = G_PANEL_ITEM(panel)->builder; other = GTK_WIDGET(gtk_builder_get_object(builder, "collapse")); gtk_widget_set_sensitive(other, TRUE); other = GTK_WIDGET(gtk_builder_get_object(builder, "expand")); gtk_widget_set_sensitive(other, TRUE); other = GTK_WIDGET(gtk_builder_get_object(builder, "classes")); gtk_widget_set_sensitive(other, TRUE); /* Actualisation de l'affichage */ if (panel->binary != NULL) { content = G_LOADED_CONTENT(panel->binary); g_object_ref(G_OBJECT(content)); change_symbols_panel_current_content(panel, content, content); g_object_unref(G_OBJECT(content)); } } } /****************************************************************************** * * * Paramètres : selection = sélection modifiée. * * unused = adresse non utilisée ici. * * * * Description : Réagit au changement de sélection des symboles. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void on_symbols_selection_change(GtkTreeSelection *selection, gpointer unused) { GtkTreeIter iter; /* Point de sélection */ GtkTreeModel *model; /* Modèle de gestion */ GBinSymbol *symbol; /* Symbole à traiter */ const mrange_t *range; /* Couverture dudit symbole */ GLoadedPanel *panel; /* Afficheur effectif de code */ if (gtk_tree_selection_get_selected(selection, &model, &iter)) { gtk_tree_model_get(model, &iter, SBC_SYMBOL, &symbol, -1); if (symbol != NULL) { range = g_binary_symbol_get_range(symbol); panel = get_current_view(); if (GTK_IS_DISPLAY_PANEL(panel)) gtk_display_panel_request_move(GTK_DISPLAY_PANEL(panel), get_mrange_addr(range)); g_object_unref(G_OBJECT(panel)); g_object_unref(G_OBJECT(symbol)); } } } /****************************************************************************** * * * Paramètres : panel = panneau à mettre à jour. * * old = ancien contenu chargé analysé. * * new = nouveau contenu chargé à analyser. * * * * Description : Réagit à un changement d'affichage principal de contenu. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void change_symbols_panel_current_content(GSymbolsPanel *panel, GLoadedContent *old, GLoadedContent *new) { GLoadedBinary *binary; /* Autre version de l'instance */ GtkBuilder *builder; /* Constructeur utilisé */ GtkTreeStore *store; /* Modèle de gestion */ GBinFormat *format; /* Format du binaire */ GCompDemangler *demangler; /* Décodeur privilégié associé */ GtkToggleToolButton *button; /* Bouton à encadrer */ if (G_IS_LOADED_BINARY(new)) binary = G_LOADED_BINARY(new); else binary = NULL; /* Basculement du binaire utilisé */ if (panel->binary != NULL) g_object_unref(G_OBJECT(panel->binary)); panel->binary = binary; if (panel->binary != NULL) g_object_ref(G_OBJECT(panel->binary)); /* Réinitialisation */ builder = G_PANEL_ITEM(panel)->builder; store = GTK_TREE_STORE(gtk_builder_get_object(builder, "store")); gtk_tree_store_clear(store); /* Si le panneau actif représente un binaire, actualisation de l'affichage */ if (binary != NULL) { format = G_BIN_FORMAT(g_loaded_binary_get_format(binary)); demangler = g_binary_format_get_demangler(format); panel->sep = g_compiler_demangler_get_ns_separator(demangler); g_object_unref(G_OBJECT(demangler)); g_object_unref(G_OBJECT(format)); if (panel->sep == NULL) { button = GTK_TOGGLE_TOOL_BUTTON(gtk_builder_get_object(builder, "tree_display")); gtk_widget_set_sensitive(GTK_WIDGET(button), FALSE); button = GTK_TOGGLE_TOOL_BUTTON(gtk_builder_get_object(builder, "list_display")); gtk_toggle_tool_button_set_active(button, TRUE); } else { button = GTK_TOGGLE_TOOL_BUTTON(gtk_builder_get_object(builder, "tree_display")); if (!gtk_widget_get_sensitive(GTK_WIDGET(button))) { gtk_widget_set_sensitive(GTK_WIDGET(button), TRUE); gtk_toggle_tool_button_set_active(button, TRUE); } } run_panel_update(G_UPDATABLE_PANEL(panel), PUI_0); } } /****************************************************************************** * * * Paramètres : panel = panneau à mettre à jour. * * status = barre de statut à tenir informée. * * id = identifiant pour le suivi de la progression. * * data = données complémentaire à manipuler. * * * * Description : Réagit à un changement d'affichage principal de contenu. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void reload_symbols_panel_content(const GSymbolsPanel *panel, GtkStatusStack *status, activity_id_t id, symbols_update_data *data) { GtkBuilder *builder; /* Constructeur utilisé */ GtkToggleToolButton *button; /* Mode de représentation */ builder = G_PANEL_ITEM(panel)->builder; button = GTK_TOGGLE_TOOL_BUTTON(gtk_builder_get_object(builder, "list_display")); if (gtk_toggle_tool_button_get_active(button)) reload_symbols_for_new_list_view(panel, status, id, data); else reload_symbols_for_new_tree_view(panel, status, id, data); } /* ---------------------------------------------------------------------------------- */ /* AFFICHAGE A L'AIDE D'UNE LISTE */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : panel = panneau à mettre à jour. * * status = barre de statut à tenir informée. * * id = identifiant pour le suivi de la progression. * * data = données complémentaire à manipuler. * * * * Description : Réagit à un changement d'affichage principal de contenu. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void reload_symbols_for_new_list_view(const GSymbolsPanel *panel, GtkStatusStack *status, activity_id_t id, symbols_update_data *data) { GtkBuilder *builder; /* Constructeur utilisé */ GtkTreeStore *store; /* Modèle de gestion */ GExeFormat *format; /* Format associé au binaire */ GArchProcessor *proc; /* Architecture utilisée */ MemoryDataSize size; /* Taille des localisations */ sym_iter_t *siter; /* Parcours des symboles */ GBinSymbol *symbol; /* Symbole manipulé */ cairo_surface_t *icon; /* Image associée au symbole */ regmatch_t match; /* Récupération des trouvailles*/ bool matched; /* Correspondance de sélection */ char *original; /* Etiquette brute d'origine */ char *name; /* Etiquette mise en relief */ const vmpa2t *addr; /* Localisation d'un symbole */ VMPA_BUFFER(virt); /* Version humainement lisible */ GtkTreeIter iter; /* Point d'insertion */ builder = G_PANEL_ITEM(panel)->builder; store = GTK_TREE_STORE(gtk_builder_get_object(builder, "store")); format = g_loaded_binary_get_format(panel->binary); proc = g_loaded_binary_get_processor(panel->binary); size = g_arch_processor_get_memory_size(proc); g_object_unref(G_OBJECT(proc)); siter = create_symbol_iterator(G_BIN_FORMAT(format), 0); for (symbol = get_symbol_iterator_current(siter); symbol != NULL; symbol = get_symbol_iterator_next(siter)) { switch (g_binary_symbol_get_target_type(symbol)) { case STP_ROUTINE: case STP_ENTRY_POINT: icon = G_SYMBOLS_PANEL_GET_CLASS(panel)->routine_img; break; case STP_OBJECT: icon = G_SYMBOLS_PANEL_GET_CLASS(panel)->object_img; break; default: icon = NULL; break; } if (icon == NULL) goto rsfnlv_next; matched = is_symbol_matching(data, symbol, &match); original = g_binary_symbol_get_label(symbol); if (matched) name = build_highlighted_name(original, &match, 0); else name = NULL; free(original); addr = get_mrange_addr(g_binary_symbol_get_range(symbol)); vmpa2_virt_to_string(addr, size, virt, NULL); gtk_tree_store_append(store, &iter, NULL); gtk_tree_store_set(store, &iter, SBC_SYMBOL, symbol, SBC_PICTURE, icon, SBC_NAME, name, SBC_ORIGINAL, original, SBC_ADDRESS, virt, SBC_MATCHED, false, SBC_MATCH_POINTS, 0, -1); if (matched) update_node_visibility(store, &iter, SBC_MATCHED, true); data->count++; if (name != NULL) free(name); rsfnlv_next: g_object_unref(G_OBJECT(symbol)); gtk_status_stack_update_activity_value(status, id, 1); } delete_symbol_iterator(siter); g_object_unref(G_OBJECT(format)); } /****************************************************************************** * * * Paramètres : store = gestionnaire de données pour une arborescence. * * iter = position des données traitées. * * match = correspondance avec un objet recherché. * * * * Description : Met en surbrillance les éléments recherchés dans les noms. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void update_symbol_name_in_list_view(GtkTreeStore *store, GtkTreeIter *iter, const regmatch_t *match) { GtkTreeModel *model; /* Autre vision du gestionnaire*/ char *original; /* Etiquette brute d'origine */ char *name; /* Etiquette mise en relief */ model = GTK_TREE_MODEL(store); gtk_tree_model_get(model, iter, SBC_ORIGINAL, &original, -1); name = build_highlighted_name(original, match, 0); gtk_tree_store_set(store, iter, SBC_NAME, name, -1); free(original); free(name); } /* ---------------------------------------------------------------------------------- */ /* AFFICHAGE SOUS FORME D'ARBRE */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : panel = panneau en cours de mise à jour. * * parent = point d'insertion parent à retrouver. [OUT] * * raw = nom du noeud ciblé. * * match = portion de texte à mettre en évidence. * * start = position du texte brute dans l'étiquette complète. * * * * Description : S'assure qu'un noeud donné existe bien. * * * * Retour : Point d'insertion prochain. * * * * Remarques : - * * * ******************************************************************************/ static GtkTreeIter ensure_symbol_node_exist(const GSymbolsPanel *panel, GtkTreeIter *parent, const char *raw, const regmatch_t *match, size_t start) { GtkBuilder *builder; /* Constructeur utilisé */ GtkTreeStore *store; /* Gestionnaire de données */ GtkTreeModel *model; /* Autre vision du gestionnaire*/ bool found; /* Bilan des recherches */ GtkTreeIter iter; /* Boucle de parcours */ gchar *string; /* Chaîne sélectionnée */ char *name; /* Etiquette mise en relief */ builder = G_PANEL_ITEM(panel)->builder; store = GTK_TREE_STORE(gtk_builder_get_object(builder, "store")); model = GTK_TREE_MODEL(store); found = false; if (gtk_tree_model_iter_children(model, &iter, parent)) do { gtk_tree_model_get(model, &iter, SBC_ORIGINAL, &string, -1); found = (strcmp(string, raw) == 0); g_free(string); if (found) break; } while (gtk_tree_model_iter_next(model, &iter)); if (!found) { name = build_highlighted_name(raw, match, start); gtk_tree_store_append(store, &iter, parent); gtk_tree_store_set(store, &iter, SBC_PICTURE, G_SYMBOLS_PANEL_GET_CLASS(panel)->package_img, SBC_NAME, name, SBC_ORIGINAL, raw, SBC_MATCHED, false, SBC_MATCH_POINTS, 0, -1); free(name); } return iter; } /****************************************************************************** * * * Paramètres : panel = panneau en cours de mise à jour. * * symbol = routine ou objet à intégrer. * * parent = point d'insertion parent à constituer. [OUT] * * match = portion de texte à mettre en évidence. * * last = position du dernier élément du nom de symbole. [OUT]* * * * Description : Détermine le point d'insertion parent d'une routine. * * * * Retour : true si le point n'est pas la racine, false sinon. * * * * Remarques : - * * * ******************************************************************************/ static bool find_parent_for_symbol(const GSymbolsPanel *panel, const GBinSymbol *symbol, GtkTreeIter *parent, const regmatch_t *match, size_t *last) { bool result; /* Bilan à retourner */ char *label; /* Etiquette modifiable */ char *start; /* Début de boucle de parcours */ char *token; /* Partie de texte isolée */ char *next; /* Prochaine partie à traiter */ result = false; *last = 0; label = g_binary_symbol_get_label(symbol); if (label == NULL) return false; for (start = label, token = strtok_w(&start, panel->sep); ; token = next) { next = strtok_w(&start, panel->sep); if (next == NULL) { *last = (token - label); break; } *parent = ensure_symbol_node_exist(panel, (token == label ? NULL : parent), token, match, token - label); result = true; } free(label); return result; } /****************************************************************************** * * * Paramètres : panel = panneau à mettre à jour. * * status = barre de statut à tenir informée. * * id = identifiant pour le suivi de la progression. * * data = données complémentaire à manipuler. * * * * Description : Réagit à un changement d'affichage principal de contenu. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void reload_symbols_for_new_tree_view(const GSymbolsPanel *panel, GtkStatusStack *status, activity_id_t id, symbols_update_data *data) { GtkBuilder *builder; /* Constructeur utilisé */ GtkTreeStore *store; /* Modèle de gestion */ GExeFormat *format; /* Format associé au binaire */ GArchProcessor *proc; /* Architecture utilisée */ MemoryDataSize size; /* Taille des localisations */ sym_iter_t *siter; /* Parcours des symboles */ GBinSymbol *symbol; /* Symbole manipulé */ cairo_surface_t *icon; /* Image associée au symbole */ regmatch_t match; /* Récupération des trouvailles*/ bool matched; /* Correspondance de sélection */ GtkTreeIter parent; /* Point d'insertion parent */ size_t last; /* Position du dernier élément */ char *original; /* Etiquette brute d'origine */ char *name; /* Etiquette mise en relief */ const vmpa2t *addr; /* Localisation d'un symbole */ char virt[VMPA_MAX_LEN]; /* Version humainement lisible */ GtkTreeIter iter; /* Point d'insertion */ builder = G_PANEL_ITEM(panel)->builder; store = GTK_TREE_STORE(gtk_builder_get_object(builder, "store")); format = g_loaded_binary_get_format(panel->binary); proc = g_loaded_binary_get_processor(panel->binary); size = g_arch_processor_get_memory_size(proc); g_object_unref(G_OBJECT(proc)); siter = create_symbol_iterator(G_BIN_FORMAT(format), 0); for (symbol = get_symbol_iterator_current(siter); symbol != NULL; symbol = get_symbol_iterator_next(siter)) { switch (g_binary_symbol_get_target_type(symbol)) { case STP_ROUTINE: case STP_ENTRY_POINT: icon = G_SYMBOLS_PANEL_GET_CLASS(panel)->routine_img; break; case STP_OBJECT: icon = G_SYMBOLS_PANEL_GET_CLASS(panel)->object_img; break; default: icon = NULL; break; } if (icon == NULL) goto rsfntv_next; matched = is_symbol_matching(data, symbol, &match); if (find_parent_for_symbol(panel, symbol, &parent, &match, &last)) { gtk_tree_store_set(store, &parent, SBC_PICTURE, G_SYMBOLS_PANEL_GET_CLASS(panel)->class_img, SBC_EXPAND, TRUE, -1); gtk_tree_store_append(store, &iter, &parent); } else gtk_tree_store_append(store, &iter, NULL); original = g_binary_symbol_get_label(symbol); if (matched) name = build_highlighted_name(original + last, &match, last); else name = NULL; addr = get_mrange_addr(g_binary_symbol_get_range(symbol)); vmpa2_virt_to_string(addr, size, virt, NULL); gtk_tree_store_set(store, &iter, SBC_SYMBOL, symbol, SBC_PICTURE, icon, SBC_NAME, name, SBC_ORIGINAL, original + last, SBC_ADDRESS, virt, SBC_MATCHED, false, SBC_MATCH_POINTS, 0, -1); if (matched) update_node_visibility(store, &iter, SBC_MATCHED, true); data->count++; if (name != NULL) free(name); free(original); rsfntv_next: g_object_unref(G_OBJECT(symbol)); gtk_status_stack_update_activity_value(status, id, 1); } delete_symbol_iterator(siter); g_object_unref(G_OBJECT(format)); } /****************************************************************************** * * * Paramètres : button = bouton concerné par l'action. * * panel = panneau à mettre à jour. * * * * Description : Réagit à une nouvelle demande de réorganisation. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void reorganize_symbols_tree_view(GtkToolButton *button, const GSymbolsPanel *panel) { GtkBuilder *builder; /* Constructeur utilisé */ GtkTreeView *treeview; /* Arborescence graphique */ GtkToolButton *ref_collapse; /* Bouton de référence #1 */ GtkToolButton *ref_expand; /* Bouton de référence #2 */ GtkTreeStore *store; /* Modèle de gestion */ builder = G_PANEL_ITEM(panel)->builder; treeview = GTK_TREE_VIEW(gtk_builder_get_object(builder, "treeview")); ref_collapse = GTK_TOOL_BUTTON(gtk_builder_get_object(builder, "collapse")); ref_expand = GTK_TOOL_BUTTON(gtk_builder_get_object(builder, "expand")); if (button == ref_collapse) gtk_tree_view_collapse_all(treeview); else if (button == ref_expand) gtk_tree_view_expand_all(treeview); else { store = GTK_TREE_STORE(gtk_builder_get_object(builder, "store")); gtk_tree_model_foreach(GTK_TREE_MODEL(store), (GtkTreeModelForeachFunc)show_all_classes_in_tree_view, treeview); } } /****************************************************************************** * * * Paramètres : model = modèle de gestion des éléments. * * path = chemin d'accès à l'élément courant. * * iter = itérateur courant. * * treeview = arborescence à manipuler ici. * * * * Description : Fait en sorte que toutes les classes soient affichées. * * * * Retour : FALSE pour continuer le parcours. * * * * Remarques : - * * * ******************************************************************************/ static gboolean show_all_classes_in_tree_view(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, GtkTreeView *treeview) { gboolean expand; /* Besoin en intervention */ GtkTreePath *tmp; /* Copie pour modification */ gtk_tree_model_get(model, iter, SBC_EXPAND, &expand, -1); if (expand) { tmp = gtk_tree_path_copy(path); if (gtk_tree_path_up(tmp)) gtk_tree_view_expand_to_path(treeview, tmp); gtk_tree_path_free(tmp); } return FALSE; } /****************************************************************************** * * * Paramètres : store = gestionnaire de données en arborescence. * * parent = point d'insertion parent à retrouver. [OUT] * * raw = nom du noeud ciblé. * * match = portion de texte à mettre en évidence. * * start = position du texte brute dans l'étiquette complète. * * * * Description : Actualise une partie d'un nom de symbole éclaté en noeuds. * * * * Retour : Point de mise à jour prochain. * * * * Remarques : - * * * ******************************************************************************/ static GtkTreeIter update_symbol_partial_name_in_tree_view(GtkTreeStore *store, GtkTreeIter *parent, const char *raw, const regmatch_t *match, size_t start) { GtkTreeModel *model; /* Autre vision du gestionnaire*/ bool found; /* Bilan des recherches */ GtkTreeIter iter; /* Boucle de parcours */ gchar *string; /* Chaîne sélectionnée */ char *name; /* Etiquette mise en relief */ model = GTK_TREE_MODEL(store); found = false; if (gtk_tree_model_iter_children(model, &iter, parent)) do { gtk_tree_model_get(model, &iter, SBC_ORIGINAL, &string, -1); found = (strcmp(string, raw) == 0); g_free(string); if (found) break; } while (gtk_tree_model_iter_next(model, &iter)); assert(found); name = build_highlighted_name(raw, match, start); gtk_tree_store_set(store, &iter, SBC_NAME, name, -1); free(name); return iter; } /****************************************************************************** * * * Paramètres : panel = panneau à mettre à jour. * * store = gestionnaire de données en arborescence. * * symbol = routine ou objet à intégrer. * * match = portion de texte à mettre en évidence. * * * * Description : Met en surbrillance les éléments recherchés dans les noms. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void update_symbol_name_in_tree_view(const GSymbolsPanel *panel, GtkTreeStore *store, const GBinSymbol *symbol, const regmatch_t *match) { char *label; /* Etiquette modifiable */ GtkTreeIter parent; /* Point d'analyse courant */ char *start; /* Début de boucle de parcours */ char *token; /* Partie de texte isolée */ label = g_binary_symbol_get_label(symbol); if (label != NULL) { for (start = label, token = strtok_w(&start, panel->sep); token != NULL; token = strtok_w(&start, panel->sep)) { parent = update_symbol_partial_name_in_tree_view(store, (token == label ? NULL : &parent), token, match, token - label); } free(label); } } /* ---------------------------------------------------------------------------------- */ /* FILTRAGE DES SYMBOLES PRESENTS */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : entry = entrée de texte contenant le filtre brut. * * panel = panneau assurant l'affichage des symboles. * * * * Description : Démarre l'actualisation du filtrage des symboles. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void on_symbols_filter_changed(GtkSearchEntry *entry, GSymbolsPanel *panel) { update_regex_on_search_entry_changed(entry, &G_PANEL_ITEM(panel)->filter); run_panel_update(G_UPDATABLE_PANEL(panel), PUI_1); } /****************************************************************************** * * * Paramètres : panel = panneau assurant l'affichage des symboles. * * status = barre de statut à tenir informée. * * id = identifiant pour le suivi de la progression. * * data = données complémentaire à manipuler. * * * * Description : Exécute un nouveau filtrage des symboles affichés. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void do_filtering_on_symbols(const GSymbolsPanel *panel, GtkStatusStack *status, activity_id_t id, symbols_update_data *data) { GtkBuilder *builder; /* Constructeur utilisé */ GtkTreeStore *store; /* Modèle de gestion */ GtkToggleToolButton *button; /* Mode de représentation */ gboolean as_list; /* Choix dudit mode */ gboolean filter_symbol_panel_iter(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gboolean *as_list) { GBinSymbol *symbol; /* Symbole manipulé */ regmatch_t match; /* Récupération des trouvailles*/ bool matched; /* Correspondance de sélection */ gboolean shown; /* Visibilité actuelle */ gtk_tree_model_get(model, iter, SBC_SYMBOL, &symbol, -1); if (symbol != NULL) { matched = is_symbol_matching(data, symbol, &match); if (matched) { if (*as_list) update_symbol_name_in_list_view(store, iter, &match); else update_symbol_name_in_tree_view(panel, store, symbol, &match); } gtk_tree_model_get(model, iter, SBC_MATCHED, &shown, -1); if (!matched) { if (shown) update_node_visibility(store, iter, SBC_MATCHED, false); } else { if (!shown) update_node_visibility(store, iter, SBC_MATCHED, true); } g_object_unref(G_OBJECT(symbol)); gtk_status_stack_update_activity_value(status, id, 1); } return FALSE; } builder = G_PANEL_ITEM(panel)->builder; store = GTK_TREE_STORE(gtk_builder_get_object(builder, "store")); button = GTK_TOGGLE_TOOL_BUTTON(gtk_builder_get_object(builder, "list_display")); as_list = gtk_toggle_tool_button_get_active(button); gtk_tree_model_foreach(GTK_TREE_MODEL(store), (GtkTreeModelForeachFunc)filter_symbol_panel_iter, &as_list); } /* ---------------------------------------------------------------------------------- */ /* MECANISMES DE MISE A JOUR DE PANNEAU */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : data = données complémentaire à manipuler. * * symbol = symbole à traiter. * * match = récupération des trouvailles. [OUT] * * * * Description : Détermine si un nom de symbole doit être filtré ou non. * * * * Retour : true si le symbol ne doit pas être affiché, false sinon. * * * * Remarques : - * * * ******************************************************************************/ static bool is_symbol_matching(const symbols_update_data *data, const GBinSymbol *symbol, regmatch_t *match) { bool result; /* Bilan à retourner */ #ifndef NDEBUG SymbolType type; /* Type associé au symbole */ #endif char *label; /* Etiquette à analyser */ #ifndef NDEBUG type = g_binary_symbol_get_target_type(symbol); assert(type == STP_ROUTINE || type == STP_ENTRY_POINT || type == STP_OBJECT); #endif label = g_binary_symbol_get_label(symbol); if (label == NULL) result = false; else { result = is_content_matching(data->filter, label, match); free(label); } return result; } /****************************************************************************** * * * Paramètres : panel = panneau ciblé par une mise à jour. * * uid = identifiant de la phase de traitement. * * count = nombre d'étapes à prévoir dans le traitement. [OUT] * * data = données sur lesquelles s'appuyer ensuite. [OUT] * * * * Description : Prépare une opération de mise à jour de panneau. * * * * Retour : Description du message d'information. * * * * Remarques : - * * * ******************************************************************************/ static const char *g_symbols_panel_setup(const GSymbolsPanel *panel, unsigned int uid, size_t *count, symbols_update_data **data) { const char *result; /* Message à retourner */ GBinFormat *format; /* Format du binaire */ int ret; /* Bilan de mise en place */ GtkBuilder *builder; /* Constructeur utilisé */ GtkTreeView *treeview; /* Arborescence graphique */ *data = malloc(sizeof(symbols_update_data)); switch (uid) { case PUI_0: format = G_BIN_FORMAT(g_loaded_binary_get_format(panel->binary)); g_binary_format_lock_symbols_rd(format); *count = g_binary_format_count_symbols(format); g_binary_format_unlock_symbols_rd(format); g_object_unref(G_OBJECT(format)); (*data)->count = 0; result = _("Loading symbols registered for the binary format..."); break; case PUI_1: *count = panel->count; (*data)->count = panel->count; result = _("Filtering symbols registered for the binary format..."); break; default: /* Pour GCC... */ assert(false); result = ""; break; } if (G_PANEL_ITEM(panel)->filter != NULL) { (*data)->filter = (regex_t *)malloc(sizeof(regex_t)); ret = regcomp((*data)->filter, G_PANEL_ITEM(panel)->filter, REG_EXTENDED | REG_ICASE); assert(ret == 0); } else (*data)->filter = NULL; /* Mémorisation de tous les noeuds ouverts */ builder = G_PANEL_ITEM(panel)->builder; treeview = GTK_TREE_VIEW(gtk_builder_get_object(builder, "treeview")); void keep_track_of_expanded(GtkTreeView *tv, GtkTreePath *path, symbols_update_data *sud) { if (sud->ecount == sud->eallocated) { sud->eallocated += EXPAND_ALLOC_RANGE; sud->expanded = (char **)realloc(sud->expanded, sud->eallocated * sizeof(char *)); } sud->expanded[sud->ecount] = gtk_tree_path_to_string(path); sud->ecount++; } (*data)->expanded = NULL; (*data)->ecount = 0; (*data)->eallocated = 0; gtk_tree_view_map_expanded_rows(treeview, (GtkTreeViewMappingFunc)keep_track_of_expanded, *data); return result; } /****************************************************************************** * * * Paramètres : panel = panneau ciblé par une mise à jour. * * uid = identifiant de la phase de traitement. * * data = données préparées par l'appelant. * * * * Description : Bascule l'affichage d'un panneau avant mise à jour. * * * * Retour : - * * * * Remarques : Cette fonction est appelée depuis le contexte principal. * * * ******************************************************************************/ static void g_symbols_panel_introduce(const GSymbolsPanel *panel, unsigned int uid, symbols_update_data *data) { GtkBuilder *builder; /* Constructeur utilisé */ GtkTreeView *treeview; /* Arborescence graphique */ GtkTreeModel *model; /* Source de données associée */ /* Basculement de l'affichage hors ligne */ g_panel_item_switch_to_updating_mask(G_PANEL_ITEM(panel)); builder = G_PANEL_ITEM(panel)->builder; treeview = GTK_TREE_VIEW(gtk_builder_get_object(builder, "treeview")); model = gtk_tree_view_get_model(treeview); if (model != NULL) { g_object_ref(G_OBJECT(model)); gtk_tree_view_set_model(treeview, NULL); } } /****************************************************************************** * * * Paramètres : panel = panneau ciblé par une mise à jour. * * uid = identifiant de la phase de traitement. * * status = barre de statut à tenir informée. * * id = identifiant pour le suivi de la progression. * * data = données préparées par l'appelant. * * * * Description : Réalise une opération de mise à jour de panneau. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_symbols_panel_process(const GSymbolsPanel *panel, unsigned int uid, GtkStatusStack *status, activity_id_t id, symbols_update_data *data) { switch (uid) { case PUI_0: reload_symbols_panel_content(panel, status, id, data); break; case PUI_1: do_filtering_on_symbols(panel, status, id, data); break; } } /****************************************************************************** * * * Paramètres : panel = panneau ciblé par une mise à jour. * * uid = identifiant de la phase de traitement. * * data = données préparées par l'appelant. * * * * Description : Bascule l'affichage d'un panneau après mise à jour. * * * * Retour : - * * * * Remarques : Cette fonction est appelée depuis le contexte principal. * * * ******************************************************************************/ static void g_symbols_panel_conclude(GSymbolsPanel *panel, unsigned int uid, symbols_update_data *data) { GtkBuilder *builder; /* Constructeur utilisé */ GtkTreeView *treeview; /* Arborescence graphique */ GtkTreeModel *model; /* Source de données associée */ size_t i; /* Boucle de parcours */ GtkTreePath *path; /* Chemin d'accès à un noeud */ GtkToggleToolButton *button; /* Mode de représentation */ if (g_atomic_int_get(&G_PANEL_ITEM(panel)->switched) > 1) goto skip_this_step; /* Mise à jour des compteurs */ panel->count = data->count; /* Basculement de l'affichage en ligne */ builder = G_PANEL_ITEM(panel)->builder; treeview = GTK_TREE_VIEW(gtk_builder_get_object(builder, "treeview")); model = GTK_TREE_MODEL(gtk_builder_get_object(builder, "filter")); g_object_ref(G_OBJECT(model)); gtk_tree_view_set_model(treeview, model); for (i = 0; i < data->ecount; i++) { path = gtk_tree_path_new_from_string(data->expanded[i]); gtk_tree_view_expand_to_path(treeview, path); gtk_tree_path_free(path); } /* Réorganisation des symboles ? */ button = GTK_TOGGLE_TOOL_BUTTON(gtk_builder_get_object(builder, "list_display")); if (!gtk_toggle_tool_button_get_active(button)) reorganize_symbols_tree_view(NULL, panel); skip_this_step: g_panel_item_switch_to_updated_content(G_PANEL_ITEM(panel)); } /****************************************************************************** * * * Paramètres : panel = panneau ciblé par une mise à jour. * * uid = identifiant de la phase de traitement. * * data = données en place à nettoyer avant suppression. * * * * Description : Supprime les données dynamiques utilisées à la mise à jour. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_symbols_panel_clean_data(GUpdatablePanel *panel, unsigned int uid, symbols_update_data *data) { size_t i; /* Boucle de parcours */ if (data->filter != NULL) { regfree(data->filter); free(data->filter); } for (i = 0; i < data->ecount; i++) g_free(data->expanded[i]); if (data->expanded != NULL) free(data->expanded); }