From b7347c96930027fc11b9c5038157f972d58a41bf Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Fri, 24 Mar 2017 08:38:31 +0100
Subject: Built routine digest for tooltip hints.

---
 ChangeLog                    |  22 ++++
 src/analysis/routine.c       | 252 +++++++++++++++++++++++++++++++++++++++++++
 src/analysis/routine.h       |   8 ++
 src/arch/processor.c         |   4 +-
 src/format/symbol.c          |   4 +
 src/glibext/gbufferview.c    |  64 +++++++++++
 src/glibext/gbufferview.h    |   4 +
 src/gtkext/gtkbinarystrip.c  |   4 +-
 src/gtkext/gtkblockdisplay.c | 113 ++++++++++++++++++-
 9 files changed, 470 insertions(+), 5 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index f41802b..d24904f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,25 @@
+17-03-24  Cyrille Bagard <nocbos@gmail.com>
+
+	* src/analysis/routine.c:
+	* src/analysis/routine.h:
+	Build routine digest for tooltip hints.
+
+	* src/arch/processor.c:
+	Add an assertion on provided instructions.
+
+	* src/format/symbol.c:
+	Add some extra 'TODO' marks.
+
+	* src/glibext/gbufferview.c:
+	* src/glibext/gbufferview.h:
+	Provide the GObject creator at a given location.
+
+	* src/gtkext/gtkbinarystrip.c:
+	Cosmetic.
+
+	* src/gtkext/gtkblockdisplay.c:
+	Show hints as tooltips.
+
 17-03-22  Cyrille Bagard <nocbos@gmail.com>
 
 	* src/arch/post.c:
diff --git a/src/analysis/routine.c b/src/analysis/routine.c
index 8322acf..089d287 100644
--- a/src/analysis/routine.c
+++ b/src/analysis/routine.c
@@ -26,9 +26,14 @@
 
 #include <malloc.h>
 #include <string.h>
+#include <stdio.h>
 #include <stdlib.h>
 
 
+#include <i18n.h>
+
+
+#include "../arch/raw.h"
 #include "../common/extstr.h"
 
 
@@ -1130,3 +1135,250 @@ void g_binary_routine_print_code(const GBinRoutine *routine, GLangOutput *lang,
 
 }
 #endif
+
+
+
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : routine = routine à mettre à jour.                           *
+*                binary  = informations relatives au binaire chargé.          *
+*                                                                             *
+*  Description : Construit un petit résumé concis de la routine.              *
+*                                                                             *
+*  Retour      : Chaîne de caractères à libérer après usage.                  *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+char *g_binary_routine_build_tooltip(const GBinRoutine *routine, const GLoadedBinary *binary)
+{
+    char *result;                           /* Description à retourner     */
+    unsigned int ins_count;                 /* Quantité d'instructions     */
+    unsigned int call_count;                /* Quantité d'appels           */
+    char *call_info;                        /* Détails des appels          */
+    unsigned int string_count;              /* Quantité de chaînes         */
+    char *string_info;                      /* Détails des chaînes         */
+    GArchProcessor *proc;                   /* Architecture utilisée       */
+    GBufferCache *cache;                    /* Tampon de désassemblage     */
+    instr_iter_t *iter;                     /* Parcours local d'adresses   */
+    GArchInstruction *instr;                /* Instruction correspondante  */
+    instr_link_t *dests;                    /* Instr. visées par une autre */
+    size_t dcount;                          /* Nombre de liens de dest.    */
+    size_t i;                               /* Boucle de parcours          */
+    const mrange_t *irange;                 /* Emplacement d'instruction   */
+    size_t index;                           /* Indice de ligne à traiter   */
+    GBufferLine *line;                      /* Ligne présente à l'adresse  */
+    char *info;                             /* Ligne d'information créée   */
+    size_t blk_count;                       /* Nombre de blocs basiques    */
+
+    result = NULL;
+
+    ins_count = 0;
+
+    call_count = 0;
+    call_info = NULL;
+
+    string_count = 0;
+    string_info = NULL;
+
+    proc = g_loaded_binary_get_processor(binary);
+    cache = g_loaded_binary_get_disassembled_cache(binary);
+
+    /* Parcours des instructions */
+
+    iter = g_arch_processor_get_iter_from_address(proc, get_mrange_addr(&routine->range));
+    if (iter == NULL) goto gbrbt_no_iter;
+
+    restrict_instruction_iterator(iter, &routine->range);
+
+    for (instr = get_instruction_iterator_current(iter);
+         instr != NULL;
+         instr = get_instruction_iterator_next(iter))
+    {
+        ins_count ++;
+
+        /* Appels ? */
+
+        g_arch_instruction_rlock_dest(instr);
+        dcount = g_arch_instruction_get_destinations(instr, &dests);
+
+        for (i = 0; i < dcount; i++)
+            switch (dests[i].type)
+            {
+                case ILT_CALL:
+
+                    call_count++;
+
+                    if (call_count > 6)
+                        continue;
+
+                    if (call_count == 6)
+                    {
+                        call_info = stradd(call_info, "\n ...");
+                        continue;
+                    }
+
+                    irange = g_arch_instruction_get_range(instr);
+
+                    index = g_buffer_cache_find_index_by_addr(cache, get_mrange_addr(irange), true);
+
+                    index = g_buffer_cache_look_for_flag(cache, index, BLF_HAS_CODE);
+
+                    line = g_buffer_cache_find_line_by_index(cache, index);
+
+                    if (line != NULL)
+                    {
+                        info = g_buffer_line_get_text(line, BLC_ASSEMBLY_HEAD, BLC_COUNT, true);
+                        g_object_unref(G_OBJECT(line));
+                    }
+
+                    else
+                        info = NULL;
+
+                    if (call_info != NULL)
+                        call_info = stradd(call_info, "\n");
+
+                    if (info != NULL)
+                    {
+                        call_info = stradd(call_info, " - ");
+                        call_info = stradd(call_info, info);
+                        free(info);
+                    }
+
+                    else
+                        call_info = stradd(call_info, " - ???");
+
+                    break;
+
+                case ILT_REF:
+
+                    if (!G_IS_RAW_INSTRUCTION(dests[i].linked))
+                        continue;
+
+                    if (!g_raw_instruction_is_string(G_RAW_INSTRUCTION(dests[i].linked)))
+                        continue;
+
+                    string_count++;
+
+                    if (string_count > 6)
+                        continue;
+
+                    if (string_count == 6)
+                    {
+                        string_info = stradd(string_info, "\n ...");
+                        continue;
+                    }
+
+                    irange = g_arch_instruction_get_range(dests[i].linked);
+
+                    index = g_buffer_cache_find_index_by_addr(cache, get_mrange_addr(irange), true);
+
+                    index = g_buffer_cache_look_for_flag(cache, index, BLF_HAS_CODE);
+
+                    line = g_buffer_cache_find_line_by_index(cache, index);
+
+                    if (line != NULL)
+                    {
+                        info = g_buffer_line_get_text(line, BLC_ASSEMBLY, BLC_COUNT, true);
+                        g_object_unref(G_OBJECT(line));
+                    }
+
+                    else
+                        info = NULL;
+
+                    if (string_info != NULL)
+                        string_info = stradd(string_info, "\n");
+
+                    if (info != NULL)
+                    {
+                        string_info = stradd(string_info, " - ");
+                        string_info = stradd(string_info, info);
+                        free(info);
+                    }
+
+                    else
+                        string_info = stradd(string_info, " - ???");
+
+                    break;
+
+                default:
+                    break;
+
+            }
+
+        g_arch_instruction_runlock_dest(instr);
+
+        g_object_unref(G_OBJECT(instr));
+
+    }
+
+    delete_instruction_iterator(iter);
+
+    /* Construction du résumé */
+
+    if (ins_count > 1)
+        asprintf(&result, "%u %s, ", ins_count, _("instructions"));
+    else
+        asprintf(&result, "%u %s, ", ins_count, _("instruction"));
+
+    blk_count = g_block_list_count_blocks(routine->blocks);
+
+    if (blk_count > 1)
+        asprintf(&info, "%zu %s", blk_count, _("basic blocks"));
+    else
+        asprintf(&info, "%zu %s", blk_count, _("basic block"));
+
+    result = stradd(result, info);
+    free(info);
+
+    result = stradd(result, "\n");
+
+    if (call_count > 1)
+        asprintf(&info, "%u %s, ", call_count, _("calls"));
+    else
+        asprintf(&info, "%u %s, ", call_count, _("call"));
+
+    result = stradd(result, info);
+    free(info);
+
+    if (string_count > 1)
+        asprintf(&info, "%u %s", string_count, _("strings"));
+    else
+        asprintf(&info, "%u %s", string_count, _("string"));
+
+    result = stradd(result, info);
+    free(info);
+
+    if (call_count > 0)
+    {
+        result = stradd(result, "\n\n");
+        result = stradd(result, call_count > 1 ? _("Calls:") : _("Call:"));
+        result = stradd(result, "\n");
+
+        result = stradd(result, call_info);
+        free(call_info);
+
+    }
+
+    if (string_count > 0)
+    {
+        result = stradd(result, "\n\n");
+        result = stradd(result, string_count > 1 ? _("Strings:") : _("String:"));
+        result = stradd(result, "\n");
+
+        result = stradd(result, string_info);
+        free(string_info);
+
+    }
+
+ gbrbt_no_iter:
+
+    g_object_unref(G_OBJECT(cache));
+    g_object_unref(G_OBJECT(proc));
+
+    return result;
+
+}
diff --git a/src/analysis/routine.h b/src/analysis/routine.h
index aab0818..e880118 100644
--- a/src/analysis/routine.h
+++ b/src/analysis/routine.h
@@ -30,8 +30,10 @@
 #include <sys/types.h>
 
 
+#include "binary.h"
 #include "variable.h"
 #include "disass/block.h"
+#include "../arch/processor.h"
 //#include "../arch/instruction.h"
 
 
@@ -187,4 +189,10 @@ char *_g_binary_routine_to_string(const GBinRoutine *, Routine2StringOptions);
 
 
 
+
+/* Construit un petit résumé concis de la routine. */
+char *g_binary_routine_build_tooltip(const GBinRoutine *, const GLoadedBinary *);
+
+
+
 #endif  /* _ANALYSIS_ROUTINE_H */
diff --git a/src/arch/processor.c b/src/arch/processor.c
index ba46e73..3a049b1 100644
--- a/src/arch/processor.c
+++ b/src/arch/processor.c
@@ -485,9 +485,9 @@ GArchInstruction *g_arch_processor_get_instruction(const GArchProcessor *proc, s
         assert(index < proc->instr_count);
 
         result = proc->instructions[index];
+        assert(result != NULL);
 
-        if (result != NULL)
-            g_object_ref(G_OBJECT(result));
+        g_object_ref(G_OBJECT(result));
 
     }
 
diff --git a/src/format/symbol.c b/src/format/symbol.c
index 898ce9a..789dd86 100644
--- a/src/format/symbol.c
+++ b/src/format/symbol.c
@@ -645,6 +645,8 @@ GBinRoutine *g_binary_symbol_get_routine(const GBinSymbol *symbol)
 {
     /* TODO : rajouter des assert() sur le type de symbole */
 
+    /* TODO : ref() */
+
     return symbol->extra.routine;
 
 }
@@ -666,6 +668,8 @@ GArchInstruction *g_binary_symbol_get_instruction(const GBinSymbol *symbol)
 {
     /* TODO : rajouter des assert() sur le type de symbole */
 
+    /* TODO : ref() */
+
     return symbol->extra.instr;
 
 }
diff --git a/src/glibext/gbufferview.c b/src/glibext/gbufferview.c
index 6a5b6d4..e134118 100644
--- a/src/glibext/gbufferview.c
+++ b/src/glibext/gbufferview.c
@@ -893,6 +893,70 @@ bool g_buffer_view_move_caret(GBufferView *view, bool ctrl, GdkScrollDirection d
 
 
 
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : view    = vue de tampon à mettre à jour.                     *
+*                x       = abscisse de la zone principale à traiter.          *
+*                y       = ordonnée de la zone principale à traiter.          *
+*                display = règles d'affichage des colonnes modulables.        *
+*                                                                             *
+*  Description : Trouve le créateur à l'origine d'un emplacement donné.       *
+*                                                                             *
+*  Retour      : Créateur trouvé ou NULL si aucun.                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+GObject *g_buffer_view_find_creator(GBufferView *view, gint x, gint y, const bool *display)
+{
+    GObject *result;                        /* Trouvaille à faire remonter */
+    gint text_pos;                          /* Abscisse de départ du texte */
+    gint lheight;                           /* Hauteur d'une ligne         */
+    size_t index;                           /* Indice de ligne de tampon   */
+    GBufferLine *line;                      /* Ligne à la position courante*/
+    line_width_summary summary;             /* Résumé concis des largeurs  */
+
+    result = NULL;
+
+    /* Zone d'intervention bornée ! */
+
+    text_pos = g_buffer_cache_get_text_position(view->cache);
+
+    if (x < text_pos)
+        goto gbvfc_done;
+
+    /* Détermination de la ligne concernée */
+
+    lheight = g_buffer_cache_get_line_height(view->cache);
+    index = y / lheight;
+
+    index += view->first;
+
+    if (index > view->last)
+        goto gbvfc_done;
+
+    line = g_buffer_cache_find_line_by_index(view->cache, index);
+
+    assert(line != NULL);
+
+    /* Recherche d'un segment et de son empreinte */
+
+    g_width_tracker_get_local_width_summary(view->tracker, index, &summary);
+
+    x -= text_pos;
+
+    result = g_buffer_line_get_creator_at(line, &summary, display, (gint []) { 0 }, &x, GDK_SCROLL_LEFT, false);
+
+    g_object_unref(G_OBJECT(line));
+
+ gbvfc_done:
+
+    return result;
+
+}
+
+
 
 
 
diff --git a/src/glibext/gbufferview.h b/src/glibext/gbufferview.h
index 82995d1..6336fb4 100644
--- a/src/glibext/gbufferview.h
+++ b/src/glibext/gbufferview.h
@@ -85,6 +85,10 @@ bool g_buffer_view_move_caret(GBufferView *, bool, GdkScrollDirection, const boo
 
 
 
+/* Trouve le créateur à l'origine d'un emplacement donné. */
+GObject *g_buffer_view_find_creator(GBufferView *, gint, gint, const bool *);
+
+
 
 /* Supprime toute mise en évidence de segments. */
 bool g_buffer_view_unhighlight_segments(GBufferView *);
diff --git a/src/gtkext/gtkbinarystrip.c b/src/gtkext/gtkbinarystrip.c
index d18a6ab..bc5a6ec 100644
--- a/src/gtkext/gtkbinarystrip.c
+++ b/src/gtkext/gtkbinarystrip.c
@@ -418,7 +418,9 @@ static gboolean gtk_binary_strip_query_tooltip(GtkWidget *widget, gint x, gint y
         g_object_unref(G_OBJECT(format));
 
     }
-    else result = FALSE;
+
+    else
+        result = FALSE;
 
     return result;
 
diff --git a/src/gtkext/gtkblockdisplay.c b/src/gtkext/gtkblockdisplay.c
index 26dbada..9698ee5 100644
--- a/src/gtkext/gtkblockdisplay.c
+++ b/src/gtkext/gtkblockdisplay.c
@@ -25,6 +25,7 @@
 
 
 #include "gtkbufferdisplay-int.h"
+#include "../arch/target.h"
 
 
 
@@ -62,6 +63,9 @@ static void gtk_block_display_finalize(GtkBlockDisplay *);
 /* Assure la gestion des clics de souris sur le composant. */
 static gboolean gtk_block_display_button_press(GtkWidget *, GdkEventButton *);
 
+/* Prépare l'affichage d'une astuce. */
+static gboolean gtk_block_display_query_tooltip(GtkWidget *, gint, gint, gboolean, GtkTooltip *);
+
 /* Redessine l'affichage suite à un changement visuel. */
 static gboolean gtk_block_display_need_redraw(GtkBlockDisplay *, GBufferView *);
 
@@ -104,6 +108,7 @@ static void gtk_block_display_class_init(GtkBlockDisplayClass *class)
     widget_class = GTK_WIDGET_CLASS(class);
 
     widget_class->button_press_event = gtk_block_display_button_press;
+    widget_class->query_tooltip = gtk_block_display_query_tooltip;
 
     panel_class = GTK_DISPLAY_PANEL_CLASS(class);
 
@@ -128,7 +133,7 @@ static void gtk_block_display_class_init(GtkBlockDisplayClass *class)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : view = composant GTK à initialiser.                          *
+*  Paramètres  : display = composant GTK à initialiser.                       *
 *                                                                             *
 *  Description : Procède à l'initialisation de l'afficheur de bloc assembleur.*
 *                                                                             *
@@ -138,8 +143,13 @@ static void gtk_block_display_class_init(GtkBlockDisplayClass *class)
 *                                                                             *
 ******************************************************************************/
 
-static void gtk_block_display_init(GtkBlockDisplay *view)
+static void gtk_block_display_init(GtkBlockDisplay *display)
 {
+    GObject *object;                        /* Autre version de l'instance */
+
+    object = G_OBJECT(display);
+
+    g_object_set(object, "has-tooltip", TRUE, NULL);
 
 }
 
@@ -255,6 +265,105 @@ static gboolean gtk_block_display_button_press(GtkWidget *widget, GdkEventButton
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : widget   = composant GTK visé par l'opération.               *
+*                x        = abscisse de la position du message.               *
+*                y        = ordonnée de la position du message.               *
+*                keyboard = indique une demande suite à obtiention du focus.  *
+*                tooltip  = astuce à compléter. [OUT]                         *
+*                                                                             *
+*  Description : Prépare l'affichage d'une astuce.                            *
+*                                                                             *
+*  Retour      : TRUE pour un affichage validé, FALSE sinon.                  *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static gboolean gtk_block_display_query_tooltip(GtkWidget *widget, gint x, gint y, gboolean keyboard, GtkTooltip *tooltip)
+{
+    gboolean result;                        /* Bilan à retourner           */
+    GtkBlockDisplay *display;               /* Autre version du composant  */
+    GtkDisplayPanel *panel;                 /* Version racine du composant */
+    gint real_x;                            /* Abscisse absolue réelle     */
+    gint real_y;                            /* Ordonnée absolue réelle     */
+    GObject *creator;                       /* Origine du segment pointé   */
+    GBinSymbol *symbol;                     /* Eventuel symbole survolé    */
+    GTargetOperand *operand;                /* Operande associé            */
+    phys_t diff;                            /* Différence avec la base     */
+    SymbolType stype;                       /* Type de symbole identifié   */
+    GBinRoutine *routine;                   /* Routine à manipuler         */
+    char *info;                             /* Information à faire paraître*/
+
+    if (keyboard) return FALSE;
+
+    result = FALSE;
+
+    display = GTK_BLOCK_DISPLAY(widget);
+    panel = GTK_DISPLAY_PANEL(display);
+
+    real_x = x;
+    real_y = y;
+    gtk_display_panel_compute_real_coord(panel, &real_x, &real_y);
+
+    creator = g_buffer_view_find_creator(GTK_BUFFER_DISPLAY(display)->view, real_x, real_y, panel->display);
+
+    if (creator != NULL)
+    {
+        symbol = NULL;
+
+        if (!G_IS_TARGET_OPERAND(creator))
+            goto gbdqt_done;
+
+        operand = G_TARGET_OPERAND(creator);
+
+        symbol = g_target_operand_get_symbol(operand, &diff);
+
+        if (symbol == NULL || diff != 0)
+            goto gbdqt_done;
+
+        stype = g_binary_symbol_get_target_type(symbol);
+
+        switch (stype)
+        {
+            case STP_ROUTINE:
+            case STP_ENTRY_POINT:
+
+                routine = g_binary_symbol_get_routine(symbol);
+
+                info = g_binary_routine_build_tooltip(routine, panel->binary);
+
+                //g_object_unref(G_OBJECT(routine));
+
+                result = (info != NULL);
+                break;
+
+            default:
+                break;
+
+        }
+
+        if (result)
+        {
+            gtk_tooltip_set_markup(tooltip, info);
+            free(info);
+        }
+
+ gbdqt_done:
+
+        if (symbol != NULL)
+            g_object_unref(G_OBJECT(symbol));
+
+        g_object_unref(creator);
+
+    }
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : display = composant GTK d'affichage.                         *
 *                view    = composant GLib interne.                            *
 *                                                                             *
-- 
cgit v0.11.2-87-g4458