/* Chrysalide - Outil d'analyse de fichiers binaires * cluster.c - mise en place de graphiques * * Copyright (C) 2016 Cyrille Bagard * * This file is part of Chrysalide. * * Chrysalide is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * Chrysalide is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Foobar. If not, see . */ #include "cluster.h" #include #include #include #include #include "../gtkblockdisplay.h" #include "../gtkbufferdisplay.h" #include "../gtkdisplaypanel.h" #include "../../common/sort.h" /* Définition du tracé d'un lien */ typedef struct _incoming_edge { InstructionLinkType type; /* Complexité du tracé */ const size_t *hslot; /* Couche horizontale réservée */ GdkPoint y; /* Position des décrochages */ GdkPoint end; /* Point d'arrivée final */ GGraphEdge *edge; /* Lien complet en préparation */ GGraphCluster *origin; /* Bloc de départ du lien */ const size_t *leaving_index; /* Indice du point de départ */ } incoming_edge; /* Détails sur le départ d'un lien */ typedef struct _leaving_edge { GdkPoint start; /* Point de départ d'un lien */ size_t index; /* Indice sur ligne de départ */ } leaving_edge; /* Réservation pour les lignes horizontales */ typedef struct _hspace_booking { gint start; /* Abscisse de départ de ligne */ size_t index; /* Indice de rangée verticale */ } hspace_booking; /* Découpage vertical */ typedef struct _graph_rank { unsigned int level; /* Niveau des blocs */ hspace_booking **right2left; /* Réservations de D -> G */ size_t r2l_count; /* Quantité de ces réservations*/ hspace_booking **left2right; /* Réservations de G -> D */ size_t l2r_count; /* Quantité de ces réservations*/ GGraphCluster **clusters; /* Ensembles de blocs */ size_t count; /* Nombre de ces ensembles */ } graph_rank; /* Mise en disposition de blocs en graphique (instance) */ struct _GGraphCluster { GObject parent; /* A laisser en premier */ size_t *parent_index; /* Indice du lien au départ */ //////////////////////////////////////// gint top_margin[2]; /* Espaces gauches et droites */ gint left_margin; /* Espaces remontant à gauche */ gint right_margin; /* Espaces remontant à droite */ //////////////////////////////////////// incoming_edge **top_anchors; /* Accroches supérieures */ size_t ta_count; /* Quantité de ces accroches */ GBasicBlock *block; /* Bloc d'origine représenté */ GtkWidget *display; /* Vue graphique associée */ GtkAllocation alloc; /* Emplacement final du bloc */ leaving_edge **bottom_anchors; /* Accroches inférieures */ size_t ba_count; /* Quantité de ces accroches */ bool has_straight; /* Présence d'une ligne droite */ unsigned int straight_level; /* Rang atteint en ligne droite*/ size_t straight_index; /* Indice du lien vertical */ graph_rank *ranks; /* Répartition verticale */ size_t ranks_count; /* Nombre de divisions */ }; /* Mise en disposition de blocs en graphique (classe) */ struct _GGraphClusterClass { GObjectClass parent; /* A laisser en premier */ }; /* Espace minimal horizontal entre les blocs */ #define HORIZONTAL_MARGIN 20 /* Espace minimal vertical entre les blocs */ #define VERTICAL_MARGIN 15 /* Espace minimal entre les liens */ #define LINK_MARGIN 10 /* Initialise la classe des mises en disposition graphique. */ static void g_graph_cluster_class_init(GGraphClusterClass *); /* Initialise une mise en disposition de blocs en graphique. */ static void g_graph_cluster_init(GGraphCluster *); /* Supprime toutes les références externes. */ static void g_graph_cluster_dispose(GGraphCluster *); /* Procède à la libération totale de la mémoire. */ static void g_graph_cluster_finalize(GGraphCluster *); /* Complète un graphique avec un sous-ensemble de blocs. */ static void g_graph_cluster_add_sub(GGraphCluster *, GGraphCluster *, GBasicBlock *); /* Organisation la disposition d'un ensemble de blocs basiques. */ static void g_graph_cluster_dispatch_x(GGraphCluster *); /* Détermine une position pour un ensemble de blocs basiques. */ //static void g_graph_cluster_set_x(GGraphCluster *, gint); /* Décalle vers la droite un ensemble de blocs basiques. */ static void g_graph_cluster_offset_x(GGraphCluster *, gint); /* Décalle vers le bas un ensemble de blocs basiques. */ static void g_graph_cluster_set_y(GGraphCluster *, gint); /* Recherche le bloc basique à l'extrémité d'un lien. */ //static GGraphCluster *g_graph_cluster_find_lower_dest_cluster(const GGraphCluster *, const GArchInstruction *); /* Met en place les embryons de liens nécessaires. */ static void g_graph_cluster_define_links(GGraphCluster *, GHashTable *); /* Calcule l'abscisse d'un lien à son départ d'un bloc. */ static gint g_graph_cluster_compute_leaving_link_position(const GGraphCluster *, size_t); /* Détermine les abscisses de tous les liens en place. */ static void g_graph_cluster_compute_link_x_positions(GGraphCluster *); /* Réserve de l'espace vertical pour les lignes horizontales. */ static void g_graph_cluster_book_hspace_for_links(GGraphCluster *); /* Détermine les ordonnées de tous les liens en place. */ static void g_graph_cluster_compute_link_y_positions(GGraphCluster *); /* Applique les positions calculées pour chaque lien graphique. */ static void g_graph_cluster_resolve_links(const GGraphCluster *); /* Indique le type définit par la GLib pour les mises en disposition graphique. */ G_DEFINE_TYPE(GGraphCluster, g_graph_cluster, G_TYPE_OBJECT); /****************************************************************************** * * * Paramètres : klass = classe à initialiser. * * * * Description : Initialise la classe des mises en disposition graphique. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_graph_cluster_class_init(GGraphClusterClass *klass) { GObjectClass *object; /* Autre version de la classe */ object = G_OBJECT_CLASS(klass); object->dispose = (GObjectFinalizeFunc/* ! */)g_graph_cluster_dispose; object->finalize = (GObjectFinalizeFunc)g_graph_cluster_finalize; } /****************************************************************************** * * * Paramètres : cluster = instance à initialiser. * * * * Description : Initialise une mise en disposition de blocs en graphique. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_graph_cluster_init(GGraphCluster *cluster) { } /****************************************************************************** * * * Paramètres : cluster = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_graph_cluster_dispose(GGraphCluster *cluster) { g_object_unref(G_OBJECT(cluster->block)); g_object_unref(G_OBJECT(cluster->display)); G_OBJECT_CLASS(g_graph_cluster_parent_class)->dispose(G_OBJECT(cluster)); } /****************************************************************************** * * * Paramètres : cluster = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_graph_cluster_finalize(GGraphCluster *cluster) { G_OBJECT_CLASS(g_graph_cluster_parent_class)->finalize(G_OBJECT(cluster)); } /****************************************************************************** * * * Paramètres : binary = binaire charger dont le code est à représenter.* * list = ensemble de blocs basiques à manipuler. * * index = indice du bloc principal à mettre en place. * * highlighted = gestionnaire de surbrillance pour segments. * * * * Description : Construit un graphique à partir de blocs basiques. * * * * Retour : Adresse de la structure mise en place. * * * * Remarques : - * * * ******************************************************************************/ GGraphCluster *g_graph_cluster_new(GLoadedBinary *binary, const GBlockList *list, size_t index, segcnt_list *highlighted) { GGraphCluster *result; /* Structure à retourner */ vmpa2t first; /* Début d'un groupe de lignes */ vmpa2t last; /* Fin d'un groupe de lignes */ GBufferCache *cache; /* Tampon brut à découper */ GBufferView *view; /* Partie affichée du tampon */ GtkRequisition requisition; /* Taille à l'écran actuelle */ result = g_object_new(G_TYPE_GRAPH_CLUSTER, NULL); /* Encapsulation du bloc d'entrée */ result->block = g_block_list_get_block(list, index); g_object_ref(G_OBJECT(result->block)); result->display = gtk_block_display_new(); gtk_widget_show(result->display); gtk_display_panel_attach_binary(GTK_DISPLAY_PANEL(result->display), binary, BVW_GRAPH); gtk_display_panel_show_border(GTK_DISPLAY_PANEL(result->display), true); /* Restriction au bloc basique */ g_basic_block_get_boundary_addresses(result->block, &first, &last); cache = g_loaded_binary_get_disassembled_cache(binary); view = g_buffer_view_new(cache, highlighted); g_buffer_view_restrict(view, &first, &last); gtk_buffer_display_set_view(GTK_BUFFER_DISPLAY(result->display), view); /* Détermination d'une position initiale centrée */ gtk_widget_get_preferred_size(result->display, NULL, &requisition); result->alloc.x = -requisition.width / 2; result->alloc.y = 0; result->alloc.width = requisition.width; result->alloc.height = requisition.height; printf("BLOCK INIT :: %d <> %d\n", result->alloc.x, result->alloc.width); return result; } /****************************************************************************** * * * Paramètres : cluster = graphique de blocs à compléter. * * sub = sous-ensemble à intégrer. * * block = bloc basique de départ du sous-ensemble. * * * * Description : Complète un graphique avec un sous-ensemble de blocs. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_graph_cluster_add_sub(GGraphCluster *cluster, GGraphCluster *sub, GBasicBlock *block) { unsigned int level; /* Niveau du nouvel ensemble */ size_t i; /* Boucle de parcours */ graph_rank new; /* Nouvel étage à insérer */ graph_rank *rank; /* Nouvel étage à compléter */ level = g_basic_block_get_rank(block); for (i = 0; i < cluster->ranks_count; i++) if (cluster->ranks[i].level == level) break; if (i == cluster->ranks_count) { new.level = level; new.right2left = NULL; new.r2l_count = 0; new.left2right = NULL; new.l2r_count = 0; new.clusters = (GGraphCluster **)calloc(1, sizeof(GGraphCluster *)); new.count = 1; new.clusters[0] = sub; int cmp_graph_rank(const graph_rank *a, const graph_rank *b) { if (a->level < b->level) return -1; else if (a->level > b->level) return 1; else return 0; } cluster->ranks = qinsert(cluster->ranks, &cluster->ranks_count, sizeof(graph_rank), (__compar_fn_t)cmp_graph_rank, &new); } else { rank = &cluster->ranks[i]; rank->count++; rank->clusters = (GGraphCluster **)realloc(rank->clusters, sizeof(GGraphCluster *) * rank->count); rank->clusters[rank->count - 1] = sub; } } /****************************************************************************** * * * Paramètres : cluster = graphique de blocs à manipuler. * * * * Description : Organisation la disposition d'un ensemble de blocs basiques. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_graph_cluster_dispatch_x(GGraphCluster *cluster) { size_t k; /* Boucle de parcours #1 */ const graph_rank *rank; /* Accès confortable au rang */ size_t j; /* Boucle de parcours #2 */ gint start; /* Position initiale de départ */ size_t rcount; /* Nombre d'ensembles présents */ size_t mid; /* Position centrale de départ */ /** * A ce point, tous les blocs parents sont placés. * On est donc en mesure de réorganiser les points d'arrivée * des liens afin d'éviter les croisements : un lien qui vient * de la gauche ne doit pas arriver tout à droite ! */ int compare_incoming_edges(const incoming_edge **a, const incoming_edge **b) { int result; /* Bilan à retourner */ gint pos_a; /* Point de départ pour A */ gint pos_b; /* Point de départ pour B */ pos_a = g_graph_cluster_compute_leaving_link_position((*a)->origin, *(*a)->leaving_index); pos_b = g_graph_cluster_compute_leaving_link_position((*b)->origin, *(*b)->leaving_index); if (pos_a < pos_b) result = -1; else if (pos_a > pos_b) result = 1; else result = 0; return result; } qsort(cluster->top_anchors, cluster->ta_count, sizeof(incoming_edge *), (__compar_fn_t)compare_incoming_edges); /** * Il est désormais temps de placer tous les blocs de code inférieurs. */ void place_sub(GGraphCluster **iter, size_t loop, gint base, int dir) { size_t i; /* Boucle de parcours #2 */ GtkAllocation needed; /* Taille requise */ assert(dir == -1 || dir == 1); for (i = 0; i < loop; i++, iter += dir) { g_graph_cluster_dispatch_x(*iter); g_graph_cluster_compute_needed_alloc(*iter, &needed); if (dir > 0) g_graph_cluster_offset_x(*iter, base - needed.x); else g_graph_cluster_offset_x(*iter, base - needed.x - needed.width); base += dir * (needed.width + HORIZONTAL_MARGIN); } } for (k = 0; k < cluster->ranks_count; k++) { rank = &cluster->ranks[k]; /* Répartition autour d'une ligne verticale */ if (cluster->has_straight) { start = g_graph_cluster_compute_leaving_link_position(cluster, cluster->straight_index); if (rank->level < cluster->straight_level) { /* Répartition à gauche du lien */ for (j = rank->count; j > 0; j--) if (*rank->clusters[j - 1]->parent_index < cluster->straight_index) break; start -= HORIZONTAL_MARGIN / 2; place_sub(rank->clusters, j, start, -1); /* Répartition à droite du lien */ for (j = 0; j < rank->count; j++) if (*rank->clusters[j]->parent_index > cluster->straight_index) break; start += HORIZONTAL_MARGIN; place_sub(rank->clusters + j, rank->count - j, start, 1); } } /* Répartition homogène */ else { rcount = rank->count; if (rcount % 2 == 1) { if (rcount > 1) { mid = rcount / 2; start = rank->clusters[mid]->alloc.x - HORIZONTAL_MARGIN; place_sub(rank->clusters + mid - 1, mid, start, -1); start *= -1; place_sub(rank->clusters + mid + 1, mid, start, 1); } else g_graph_cluster_dispatch_x(rank->clusters[0]); } else { mid = rcount / 2 - 1; start = - HORIZONTAL_MARGIN / 2; place_sub(rank->clusters + mid, mid + 1, start, -1); start *= -1; place_sub(rank->clusters + mid + 1, mid + 1, start, 1); } } } } /****************************************************************************** * * * Paramètres : cluster = graphique de blocs à actualiser. * * x = position finale sur l'axe des abscisses. * * * * Description : Détermine une position pour un ensemble de blocs basiques. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ /* static void g_graph_cluster_set_x(GGraphCluster *cluster, gint x) { g_graph_cluster_offset_x(cluster, -cluster->alloc.x); g_graph_cluster_offset_x(cluster, x); } */ /****************************************************************************** * * * Paramètres : cluster = graphique de blocs à actualiser. * * offset = décallage à appliquer. * * * * Description : Décalle vers la droite un ensemble de blocs basiques. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_graph_cluster_offset_x(GGraphCluster *cluster, gint offset) { size_t i; /* Boucle de parcours #1 */ size_t j; /* Boucle de parcours #2 */ cluster->alloc.x += offset; for (i = 0; i < cluster->ranks_count; i++) for (j = 0; j < cluster->ranks[i].count; j++) g_graph_cluster_offset_x(cluster->ranks[i].clusters[j], offset); } /****************************************************************************** * * * Paramètres : cluster = graphique de blocs à actualiser. * * base = position ordonnée à appliquer. * * * * Description : Décalle vers le bas un ensemble de blocs basiques. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_graph_cluster_set_y(GGraphCluster *cluster, gint base) { size_t i; /* Boucle de parcours #1 */ const graph_rank *rank; /* Accès simplifié à la rangée */ gint max; /* Hauteur maximale rencontrée */ size_t j; /* Boucle de parcours #2 */ GGraphCluster *sub; /* Sous-ensemble traité */ GtkAllocation alloc; /* Allocation courante */ cluster->alloc.y = base; base += cluster->alloc.height; for (i = 0; i < cluster->ranks_count; i++) { rank = &cluster->ranks[i]; /* On ajoute l'espace vertical pour les lignes horizontales */ if (rank->r2l_count > rank->l2r_count) max = rank->r2l_count; else max = rank->l2r_count; base += VERTICAL_MARGIN; /** * Comme les liens purement verticaux n'entrainent pas de réservation, * il n'y a potentiellement pas toujours d'espace à ajouter. */ if (max > 0) base += ((max - 1) * LINK_MARGIN); /* On ajoute l'espace requis pour l'affichage des blocs */ base += VERTICAL_MARGIN; max = 0; for (j = 0; j < rank->count; j++) { sub = rank->clusters[j]; g_graph_cluster_set_y(sub, base); g_graph_cluster_compute_needed_alloc(sub, &alloc); if ((alloc.y + alloc.height) > max) max = alloc.y + alloc.height; } base = max; } } /****************************************************************************** * * * Paramètres : cluster = encapsulation à consulter. * * alloc = emplacement idéal pour l'affichage. * * * * Description : Détermine l'emplacement requis d'un ensemble de blocs. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_graph_cluster_compute_needed_alloc(const GGraphCluster *cluster, GtkAllocation *alloc) { GtkAllocation needed; /* Taille requise */ size_t i; /* Boucle de parcours */ size_t rcount; /* Nombre d'ensembles présents */ *alloc = cluster->alloc; for (i = 0; i < cluster->ranks_count; i++) { rcount = cluster->ranks[i].count; switch (rcount) { case 1: g_graph_cluster_compute_needed_alloc(cluster->ranks[i].clusters[0], &needed); if (needed.x < alloc->x) { alloc->width += (alloc->x - needed.x); alloc->x = needed.x; } if ((needed.x + needed.width) > (alloc->x + alloc->width)) alloc->width += needed.x + needed.width - alloc->x - alloc->width; /* La hauteur maximale n'est présente qu'avec le dernier morceau */ if ((i + 1) == cluster->ranks_count) { if ((needed.y + needed.height) > (alloc->y + alloc->height)) alloc->height += needed.y + needed.height - alloc->y - alloc->height; } break; default: assert(rcount >= 2); g_graph_cluster_compute_needed_alloc(cluster->ranks[i].clusters[0], &needed); if (needed.x < alloc->x) { alloc->width += (alloc->x - needed.x); alloc->x = needed.x; } /* La hauteur maximale n'est présente qu'avec le dernier morceau */ if ((i + 1) == cluster->ranks_count) { if ((needed.y + needed.height) > (alloc->y + alloc->height)) alloc->height += needed.y + needed.height - alloc->y - alloc->height; } g_graph_cluster_compute_needed_alloc(cluster->ranks[i].clusters[rcount - 1], &needed); if ((needed.x + needed.width) > (alloc->x + alloc->width)) alloc->width += needed.x + needed.width - alloc->x - alloc->width; /* La hauteur maximale n'est présente qu'avec le dernier morceau */ if ((i + 1) == cluster->ranks_count) { if ((needed.y + needed.height) > (alloc->y + alloc->height)) alloc->height += needed.y + needed.height - alloc->y - alloc->height; } break; } } } /****************************************************************************** * * * Paramètres : cluster = encapsulation à traiter. * * display = support de destination finale. * * * * Description : Dispose chaque noeud sur la surface de destination donnée. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_graph_cluster_place(GGraphCluster *cluster, GtkGraphDisplay *display) { size_t i; /* Boucle de parcours #1 */ size_t j; /* Boucle de parcours #2 */ g_object_ref(G_OBJECT(cluster->display)); gtk_graph_display_put(display, cluster->display, &cluster->alloc); for (i = 0; i < cluster->ta_count; i++) { g_object_ref(G_OBJECT(cluster->top_anchors[i]->edge)); gtk_graph_display_add_edge(display, cluster->top_anchors[i]->edge); } for (i = 0; i < cluster->ranks_count; i++) for (j = 0; j < cluster->ranks[i].count; j++) g_graph_cluster_place(cluster->ranks[i].clusters[j], display); } ////////////////////////////////////////////////////////// /****************************************************************************** * * * Paramètres : cluster = graphique de blocs à consulter ou remonter. * * first = première instruction du bloc basique recherché. * * * * Description : Recherche le bloc basique à l'extrémité d'un lien. * * * * Retour : Bloc graphique de destination pour un lien ou NULL si échec. * * * * Remarques : - * * * ******************************************************************************/ #if 0 static GGraphCluster *g_graph_cluster_find_lower_dest_cluster(const GGraphCluster *cluster, const GArchInstruction *first) { GGraphCluster *result; /* Trouvaille à retourner */ size_t i; /* Boucle de parcours #1 */ const graph_rank *rank; /* Confort d'accès */ size_t j; /* Boucle de parcours #2 */ GArchInstruction *instr; /* Instruction à comparer */ result = NULL; for (i = 0; i < cluster->ranks_count && result == NULL; i++) { rank = &cluster->ranks[i]; for (j = 0; j < rank->count && result == NULL; j++) { g_basic_block_get_boundary(rank->clusters[j]->block, &instr, NULL); if (instr == first) result = rank->clusters[j]; } } return result; } #endif /****************************************************************************** * * * Paramètres : cluster = graphique de blocs à actualiser. * * all = table regroupant tous les groupes créés. * * * * Description : Met en place les embryons de liens nécessaires. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_graph_cluster_define_links(GGraphCluster *cluster, GHashTable *all) { unsigned int level; /* Niveau du nouvel ensemble */ GArchInstruction *last; /* Dernière instruction du bloc*/ instr_link_t *dests; /* Instr. visées par une autre */ size_t dcount; /* Nombre de liens de dest. */ size_t i; /* Boucle de parcours #1 */ GGraphCluster *target; /* Bloc ciblé par un lien */ leaving_edge *leaving; /* Point de départ d'un lien */ unsigned int target_level; /* Rang du bloc ciblé */ size_t j; /* Boucle de parcours #2 */ size_t k; /* Boucle de parcours #3 */ incoming_edge *incoming; /* Définitions d'arrivée */ /* Au niveau du bloc courant */ level = g_basic_block_get_rank(cluster->block); g_basic_block_get_boundary(cluster->block, NULL, &last); g_arch_instruction_rlock_dest(last); dcount = g_arch_instruction_get_destinations(last, &dests); for (i = 0; i < dcount; i++) switch (dests[i].type) { case ILT_EXEC_FLOW: case ILT_JUMP: case ILT_CASE_JUMP: case ILT_JUMP_IF_TRUE: case ILT_JUMP_IF_FALSE: target = G_GRAPH_CLUSTER(g_hash_table_lookup(all, dests[i].linked)); assert(target != NULL); /* Point de départ */ leaving = (leaving_edge *)calloc(1, sizeof(leaving_edge)); cluster->bottom_anchors = (leaving_edge **)realloc(cluster->bottom_anchors, ++cluster->ba_count * sizeof(leaving_edge *)); cluster->bottom_anchors[cluster->ba_count - 1] = leaving; /* Est-ce un lien qui doit être vertical ? */ /* FIXME : trop de boucles ! */ target_level = -1; for (j = 0; j < cluster->ranks_count && target_level == -1; j++) for (k = 0; k < cluster->ranks[j].count && target_level == -1; k++) if (cluster->ranks[j].clusters[k] == target) { target_level = cluster->ranks[j].level; if (target_level > (level + 1) && target_level > cluster->straight_level) { cluster->has_straight = true; cluster->straight_level = target_level; cluster->straight_index = cluster->ba_count - 1; } } /* Point d'arrivée */ incoming = (incoming_edge *)calloc(1, sizeof(incoming_edge)); target->top_anchors = (incoming_edge **)realloc(target->top_anchors, ++target->ta_count * sizeof(incoming_edge *)); target->top_anchors[target->ta_count - 1] = incoming; /* Etablissement d'un embryon de lien */ leaving->index = cluster->ba_count - 1; incoming->type = dests[i].type; if (incoming->type == ILT_JUMP_IF_TRUE) incoming->edge = g_graph_edge_new_true(&leaving->start, &incoming->y, &incoming->end); else if (incoming->type == ILT_JUMP_IF_FALSE) incoming->edge = g_graph_edge_new_false(&leaving->start, &incoming->y, &incoming->end); else incoming->edge = g_graph_edge_new(&leaving->start, &incoming->y, &incoming->end); incoming->origin = cluster; incoming->leaving_index = &leaving->index; /* FIXME : trop de boucles ! */ target_level = -1; for (j = 0; j < cluster->ranks_count && target_level == -1; j++) for (k = 0; k < cluster->ranks[j].count && target_level == -1; k++) if (cluster->ranks[j].clusters[k] == target) { target_level = 0; target->parent_index = &leaving->index; } break; case ILT_LOOP: /* TODO */ //assert(false); break; default: break; } g_arch_instruction_runlock_dest(last); /* Déplacement d'un éventuel lien central */ if (cluster->has_straight) { size_t center; leaving_edge *tmp; if (cluster->ba_count % 2 == 0) center = cluster->ba_count / 2 - 1; else center = cluster->ba_count / 2; if (cluster->straight_index < center) { tmp = cluster->bottom_anchors[cluster->straight_index]; memmove(cluster->bottom_anchors + cluster->straight_index, cluster->bottom_anchors + cluster->straight_index + 1, (center - cluster->straight_index) * sizeof(leaving_edge *)); cluster->bottom_anchors[center] = tmp; for (i = cluster->straight_index; i <= center; i++) cluster->bottom_anchors[i]->index = i; cluster->straight_index = center; } else if (cluster->straight_index > center) { tmp = cluster->bottom_anchors[cluster->straight_index]; memmove(cluster->bottom_anchors + center + 1, cluster->bottom_anchors + center, (cluster->straight_index - center) * sizeof(leaving_edge *)); cluster->bottom_anchors[center] = tmp; for (i = center; i <= cluster->straight_index ; i++) cluster->bottom_anchors[i]->index = i; cluster->straight_index = center; } } /* Propagation de la mise en place */ for (i = 0; i < cluster->ranks_count; i++) for (j = 0; j < cluster->ranks[i].count; j++) g_graph_cluster_define_links(cluster->ranks[i].clusters[j], all); } /****************************************************************************** * * * Paramètres : cluster = graphique de blocs à consulter. * * index = indice du lien à considérer. * * * * Description : Calcule l'abscisse d'un lien à son départ d'un bloc. * * * * Retour : Abscisse à attribuer à un départ de lien. * * * * Remarques : - * * * ******************************************************************************/ static gint g_graph_cluster_compute_leaving_link_position(const GGraphCluster *cluster, size_t index) { gint result; /* Position à retourner */ gint mid_x; /* Abscisse centrale */ size_t half; /* Indice de répartition égale */ assert(index < cluster->ba_count); mid_x = cluster->alloc.x + (cluster->alloc.width / 2); half = cluster->ba_count / 2; if (cluster->ba_count % 2 == 1) { if (index < half) result = mid_x - (half - index) * LINK_MARGIN; else if (index == half) result = mid_x; else result = mid_x + (index - half) * LINK_MARGIN; } else { if (index < half) result = mid_x - LINK_MARGIN / 2 - (half - index - 1) * LINK_MARGIN; else result = mid_x + LINK_MARGIN / 2 + (index - half) * LINK_MARGIN; } return result; } /****************************************************************************** * * * Paramètres : cluster = graphique de blocs à actualiser. * * * * Description : Détermine les abscisses de tous les liens en place. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_graph_cluster_compute_link_x_positions(GGraphCluster *cluster) { gint mid_x; /* Abscisse centrale */ size_t i; /* Boucle de parcours #1 */ size_t half; /* Indice de répartition égale */ GdkPoint *pt; /* Point à actualiser */ size_t j; /* Boucle de parcours #2 */ mid_x = cluster->alloc.x + (cluster->alloc.width / 2); /* Du côté des départs... */ if (cluster->ba_count > 0) for (i = 0; i < cluster->ba_count; i++) { pt = &cluster->bottom_anchors[i]->start; pt->x = g_graph_cluster_compute_leaving_link_position(cluster, i); } /* Du côté des arrivées... */ if (cluster->ta_count > 0) { half = cluster->ta_count / 2; if (cluster->ta_count % 2 == 1) { for (i = half; i > 0; i--) { pt = &cluster->top_anchors[i - 1]->end; pt->x = mid_x - (half - i + 1) * LINK_MARGIN; } cluster->top_anchors[half]->end.x = mid_x; for (i = half + 1; i < cluster->ta_count; i++) { pt = &cluster->top_anchors[i]->end; pt->x = mid_x + (i - half) * LINK_MARGIN; } } else { for (i = half; i > 0; i--) { pt = &cluster->top_anchors[i - 1]->end; pt->x = mid_x - LINK_MARGIN / 2 - (half - i) * LINK_MARGIN; } for (i = half; i < cluster->ta_count; i++) { pt = &cluster->top_anchors[i]->end; pt->x = mid_x + LINK_MARGIN / 2 + (half - i) * LINK_MARGIN; } } } /* Propagation des déterminations */ for (i = 0; i < cluster->ranks_count; i++) for (j = 0; j < cluster->ranks[i].count; j++) g_graph_cluster_compute_link_x_positions(cluster->ranks[i].clusters[j]); } /****************************************************************************** * * * Paramètres : cluster = graphique de blocs à traiter. * * * * Description : Réserve de l'espace vertical pour les lignes horizontales. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_graph_cluster_book_hspace_for_links(GGraphCluster *cluster) { size_t i; /* Boucle de parcours #1 */ graph_rank *rank; /* Rangée à manipuler */ size_t j; /* Boucle de parcours #2 */ GGraphCluster *sub; /* Bloc inférieur à manipuler */ size_t k; /* Boucle de parcours #3 */ gint x1; /* Abscisse de départ de lien */ gint x2; /* Abscisse d'arrivée de lien */ hspace_booking *new; /* Nouvelle réservation */ for (i = 0; i < cluster->ranks_count; i++) { rank = &cluster->ranks[i]; /* Enregistrement des besoins */ for (j = 0; j < rank->count; j++) { sub = rank->clusters[j]; for (k = 0; k < sub->ta_count; k++) { g_graph_edge_get_x_borders(sub->top_anchors[k]->edge, &x1, &x2); if (x1 > x2) { new = (hspace_booking *)calloc(1, sizeof(hspace_booking)); new->start = x1; sub->top_anchors[k]->hslot = &new->index; int compare_right_to_left(const hspace_booking **a, const hspace_booking **b) { int result; /* Bilan à retourner */ if ((*a)->start > (*b)->start) result = -1; else if ((*a)->start < (*b)->start) result = 1; else { assert(false); result = 0; } return result; } rank->right2left = qinsert(rank->right2left, &rank->r2l_count, sizeof(hspace_booking *), (__compar_fn_t)compare_right_to_left, &new); } else if (x1 < x2) { new = (hspace_booking *)calloc(1, sizeof(hspace_booking)); new->start = x1; sub->top_anchors[k]->hslot = &new->index; int compare_left_to_right(const hspace_booking **a, const hspace_booking **b) { int result; /* Bilan à retourner */ if ((*a)->start < (*b)->start) result = -1; else if ((*a)->start > (*b)->start) result = 1; else { assert(false); result = 0; } return result; } rank->left2right = qinsert(rank->left2right, &rank->l2r_count, sizeof(hspace_booking *), (__compar_fn_t)compare_left_to_right, &new); } else sub->top_anchors[k]->hslot = NULL; } } /* Définition des couches */ for (j = 0; j < rank->r2l_count; j++) rank->right2left[j]->index = j; for (j = 0; j < rank->l2r_count; j++) rank->left2right[j]->index = j; /* Propagation des déterminations */ for (j = 0; j < rank->count; j++) g_graph_cluster_book_hspace_for_links(rank->clusters[j]); } } /****************************************************************************** * * * Paramètres : cluster = graphique de blocs à actualiser. * * * * Description : Détermine les ordonnées de tous les liens en place. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_graph_cluster_compute_link_y_positions(GGraphCluster *cluster) { gint y; /* Ordonnée d'application */ size_t i; /* Boucle de parcours #1 */ incoming_edge *incoming; /* Raccourci pour le confort */ size_t j; /* Boucle de parcours #2 */ /* Du côté des départs... */ if (cluster->ba_count > 0) { y = cluster->alloc.y + cluster->alloc.height; for (i = 0; i < cluster->ba_count; i++) cluster->bottom_anchors[i]->start.y = y; } /* Du côté des arrivées... */ if (cluster->ta_count > 0) { y = cluster->alloc.y; for (i = 0; i < cluster->ta_count; i++) { incoming = cluster->top_anchors[i]; incoming->end.y = y; incoming->y.y = incoming->end.y - VERTICAL_MARGIN; if (incoming->hslot != NULL) incoming->y.y -= *incoming->hslot * LINK_MARGIN; } } /* Propagation des déterminations */ for (i = 0; i < cluster->ranks_count; i++) for (j = 0; j < cluster->ranks[i].count; j++) g_graph_cluster_compute_link_y_positions(cluster->ranks[i].clusters[j]); } /****************************************************************************** * * * Paramètres : cluster = graphique de blocs à consulter. * * * * Description : Applique les positions calculées pour chaque lien graphique. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_graph_cluster_resolve_links(const GGraphCluster *cluster) { size_t i; /* Boucle de parcours #1 */ size_t j; /* Boucle de parcours #2 */ for (i = 0; i < cluster->ta_count; i++) g_graph_edge_resolve(cluster->top_anchors[i]->edge); for (i = 0; i < cluster->ranks_count; i++) for (j = 0; j < cluster->ranks[i].count; j++) g_graph_cluster_resolve_links(cluster->ranks[i].clusters[j]); } /* Liste de blocs restants à traiter */ typedef struct _pending_blocks { size_t count; /* Taille de la liste */ GBasicBlock *list[0]; /* Liste de blocs à traiter */ } pending_blocks; /* Met en place un ensemble de blocs sous forme graphique. */ static GGraphCluster *setup_graph_clusters(GLoadedBinary *, const GBlockList *, size_t , segcnt_list *, pending_blocks *, GHashTable *); /****************************************************************************** * * * Paramètres : binary = binaire charger dont le code est à représenter.* * list = ensemble de blocs basiques à manipuler. * * index = indice du bloc principal à mettre en place. * * highlighted = gestionnaire de surbrillance pour segments. * * pending = liste de blocs restant à traiter. [OUT] * * all = table regroupant tous les groupes créés. * * * * Description : Met en place un ensemble de blocs sous forme graphique. * * * * Retour : Ensemble de blocs mis en place. * * * * Remarques : - * * * ******************************************************************************/ static GGraphCluster *setup_graph_clusters(GLoadedBinary *binary, const GBlockList *list, size_t index, segcnt_list *highlighted, pending_blocks *pending, GHashTable *all) { GGraphCluster *result; /* Instance nouvelle à renvoyer*/ GBasicBlock *block; /* Bloc à manipuler */ GArchInstruction *first; /* Première instruction du bloc*/ GArchInstruction *last; /* Dernière instruction du bloc*/ #ifndef NDEBUG gboolean new; /* Bloc déjà traité ? */ #endif instr_link_t *dests; /* Instr. visées par une autre */ size_t dcount; /* Nombre de liens de dest. */ size_t i; /* Boucle de parcours #1 */ GBasicBlock *target; /* Bloc ciblé par un lien */ size_t j; /* Boucle de parcours #2 */ bool changed; /* Un ajout a été effectué ? */ const bitfield_t *dominators; /* Blocs dominant ce même bloc */ size_t next; /* Indice du prochain bloc */ GGraphCluster *sub; /* Sous-ensemble à intégrer */ result = g_graph_cluster_new(binary, list, index, highlighted); block = g_block_list_get_block(list, index); g_basic_block_get_boundary(block, &first, &last); #ifndef NDEBUG new = g_hash_table_insert(all, first, result); assert(new); #else g_hash_table_insert(all, first, result); #endif /* Détermination des blocs suivants */ g_arch_instruction_rlock_dest(last); dcount = g_arch_instruction_get_destinations(last, &dests); for (i = 0; i < dcount; i++) switch (dests[i].type) { case ILT_EXEC_FLOW: case ILT_JUMP: case ILT_CASE_JUMP: case ILT_JUMP_IF_TRUE: case ILT_JUMP_IF_FALSE: target = g_block_list_find_by_starting_instr(list, dests[i].linked); /** * Les sauts ne se font pas toujours à l'intérieur d'une même fonction. * Par exemple sous ARM : * * 00008358 : * .... * 8362: f7ff bfcf b.w 8304 <_init+0x38> * .... * */ printf(" NEW BLK :: %d -> %p\n", dests[i].type, target); if (target != NULL) { for (j = 0; j < pending->count; j++) if (pending->list[j] == target) break; if (j == pending->count) { /** * Il faut vérifier ici si la destination n'a pas déjà été * empruntée, sauf peine de faire réagir l'assertion plus * haut au moment de l'insertion. * * Le type de code à problème est le suivant : * * ... * if (...) * ... * ... * * Le code suivant le bloc conditionnel a deux origines, * qui vont chacune poursuivre le traitement vers ce code * commun. * * Et comme les origines ne sont pas dominantes, on utilise * la table globale. */ if (!g_hash_table_contains(all, dests[i].linked)) { assert((pending->count + 1) < g_block_list_count_blocks(list)); pending->list[pending->count++] = target; printf(" --push-- %d -> %p\n", dests[i].type, target); } } do { size_t k; printf(" --stack-- "); for (k = 0; k < pending->count; k++) printf("%p ", pending->list[k]); printf("\n"); } while (0); } break; default: break; } g_arch_instruction_runlock_dest(last); /* Intégration de tous les blocs en attente */ do { changed = false; for (i = 0; i < pending->count && !changed; i++) { block = pending->list[i]; dominators = g_basic_block_get_domination(block); if (test_in_bit_field(dominators, index, 1)) { /* Dépilement */ changed = true; if ((i + 1) < pending->count) memmove(&pending->list[i], &pending->list[i + 1], (pending->count - i - 1) * sizeof(GBasicBlock *)); pending->count--; /* Intégration */ next = g_block_list_get_index(list, block); assert(next < g_block_list_count_blocks(list)); printf(" --pop-- block=%p index=%zu\n", block, next); sub = setup_graph_clusters(binary, list, next, highlighted, pending, all); g_graph_cluster_add_sub(result, sub, block); } } } while (changed); return result; } /****************************************************************************** * * * Paramètres : blocXXXXXXXXXXXXXXXXXXXXks = ensemble des blocs basiques déjà découpés. * * views = morceaux de codXXXXXXXXXXXXXXXXXxe à afficher de façon organisée. * * count = quantité de ces morceaux de code.XXXXXXXXXXXXXXXXXX * * * * Description : Construit un graphique à partir de blocs basiques. * * * * Retour : Adresse de la structure mise en place. * * * * Remarques : - * * * ******************************************************************************/ GGraphCluster *bootstrap_graph_cluster(GLoadedBinary *binary, const GBlockList *list, segcnt_list *highlighted) { GGraphCluster *result; /* Structure à retourner */ GHashTable *all; /* Collection des créations */ size_t count; /* Taille de la liste de blocs */ pending_blocks *pending; /* Suivi des blocs à traiter */ GtkAllocation needed; /* Taille requise */ all = g_hash_table_new(NULL, NULL); count = g_block_list_count_blocks(list); pending = (pending_blocks *)calloc(1, sizeof(pending_blocks) + count * sizeof(GBasicBlock *)); #if 0 do { size_t i; GBasicBlock *block; const bitfield_t *domination; GArchInstruction *first; /* Première instruction du bloc*/ GArchInstruction *last; /* Dernière instruction du bloc*/ printf("DBG // count : %zu\n", count); for (i = 0; i < count; i++) { block = g_block_list_get_block(list, i); domination = g_basic_block_get_domination(block); g_basic_block_get_boundary(block, &first, &last); printf("DBG // block [%zu] @ 0x%08x : 0x%08lx -- rank = %u\n", i, (unsigned int)first->range.addr.virtual, gfw(domination), g_basic_block_get_rank(block)); } fflush(NULL); } while (0); void show_tree(GGraphCluster *cluster, int depth) { int i; GArchInstruction *first; size_t j; size_t k; printf("TREE | "); for (i = 0; i < depth; i++) printf(" "); g_basic_block_get_boundary(cluster->block, &first, NULL); printf("cluster @ 0x%08x\n", (unsigned int)first->range.addr.virtual); for (j = 0; j < cluster->ranks_count; j++) { printf("TREE | "); for (i = 0; i < depth; i++) printf(" "); printf(" + rank %u +\n", cluster->ranks[j].level); for (k = 0; k < cluster->ranks[j].count; k++) show_tree(cluster->ranks[j].clusters[k], depth + 1); } } #endif result = setup_graph_clusters(binary, list, 0, highlighted, pending, all); free(pending); g_graph_cluster_define_links(result, all); //show_tree(result, 0); //printf("--------------\n"); /* Positionnements dans l'espace */ g_graph_cluster_dispatch_x(result); /* Réajustement vers la droite */ g_graph_cluster_compute_needed_alloc(result, &needed); g_graph_cluster_offset_x(result, -needed.x); /* Application finale sur les liens */ g_graph_cluster_compute_link_x_positions(result); g_graph_cluster_book_hspace_for_links(result); g_graph_cluster_set_y(result, 0); g_graph_cluster_compute_link_y_positions(result); g_graph_cluster_resolve_links(result); g_hash_table_unref(all); return result; }