From 27f44a381ed8c317a16df450ec00ed87bbb9a4b3 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Thu, 21 Jun 2018 22:43:23 +0200
Subject: Loaded strings from ELF files using all threads.

---
 plugins/elf/format.c  |   2 +-
 plugins/elf/loading.c | 150 +++++++++++++++++++++++++++++
 plugins/elf/loading.h |   9 ++
 plugins/elf/strings.c | 259 +++++++++++++++++++++++++++++++++++++++-----------
 plugins/elf/strings.h |   8 +-
 plugins/elf/symbols.h |   1 -
 6 files changed, 368 insertions(+), 61 deletions(-)

diff --git a/plugins/elf/format.c b/plugins/elf/format.c
index 3394ba2..8c0f0fc 100644
--- a/plugins/elf/format.c
+++ b/plugins/elf/format.c
@@ -392,7 +392,7 @@ static bool g_elf_format_analyze(GElfFormat *format, wgroup_id_t gid, GtkStatusS
     if (!load_elf_symbols(format, gid, status))
         goto gefa_error;
 
-    if (!find_all_elf_strings(format))
+    if (!find_all_elf_strings(format, gid, status))
         goto gefa_error;
 
     if (!g_executable_format_complete_loading(exe, status))
diff --git a/plugins/elf/loading.c b/plugins/elf/loading.c
index 0bb6f2b..eb992b9 100644
--- a/plugins/elf/loading.c
+++ b/plugins/elf/loading.c
@@ -49,6 +49,10 @@ struct _GElfLoading
 
     phys_t str_start;                       /* Chaînes à disposition       */
 
+    /**
+     * Gestion des informations de contexte.
+     */
+
     union
     {
         struct
@@ -73,9 +77,23 @@ struct _GElfLoading
 
         };
 
+        struct
+        {
+            phys_t global_start;            /* Départ global dans la zone  */
+            phys_t global_end;              /* Fin globale dans la zone    */
+            virt_t global_addr;             /* Adresse virtuelle initiale  */
+
+            GBinContent *content;           /* Contenu binaire à lire      */
+            const bin_t *data;              /* Contenu complet et original */
+
+        };
 
     };
 
+    /**
+     * Gestion du mode de parcours.
+     */
+
     union
     {
         struct
@@ -195,6 +213,9 @@ static void g_elf_loading_init(GElfLoading *loading)
 
 static void g_elf_loading_dispose(GElfLoading *loading)
 {
+    if (loading->kind == 2)
+        g_object_unref(G_OBJECT(loading->content));
+
     G_OBJECT_CLASS(g_elf_loading_parent_class)->dispose(G_OBJECT(loading));
 
 }
@@ -361,6 +382,64 @@ GElfLoading *g_elf_loading_new_for_applying(GElfFormat *format, sym_iter_t *iter
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : format   = ensemble d'instructions désassemblées.            *
+*                begin    = point de départ de la zone à traiter.             *
+*                end      = point d'arrivée exclu du parcours.                *
+*                gb_start = position de départ pour l'ensemble des données.   *
+*                gb_end   = position finale dans l'ensemble des données.      *
+*                addr     = adresse virtuelle de la position initiale.        *
+*                id       = identifiant du message affiché à l'utilisateur.   *
+*                callback = routine de traitements particuliers.              *
+*                                                                             *
+*  Description : Crée une tâche de chargement de chaînes pour ELF différée.   *
+*                                                                             *
+*  Retour      : Tâche créée.                                                 *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+GElfLoading *g_elf_loading_new_for_strings(GElfFormat *format, phys_t begin, phys_t end, phys_t gb_start, phys_t gb_end, virt_t addr, activity_id_t id, elf_loading_cb callback)
+{
+    GElfLoading *result;                    /* Tâche à retourner           */
+    vmpa2t pos;                             /* Tête de lecture             */
+
+    result = g_object_new(G_TYPE_ELF_LOADING, NULL);
+
+    result->format = format;
+
+    result->global_start = gb_start;
+    result->global_end = gb_end;
+    result->global_addr = addr;
+
+    result->content = g_binary_format_get_content(G_BIN_FORMAT(format));
+
+    init_vmpa(&pos, gb_start, addr);
+
+    result->data = g_binary_content_get_raw_access(result->content, &pos, gb_end - gb_start);
+    if (result->data == NULL) goto no_data;
+
+    result->begin = begin;
+    result->end = end;
+    result->callback_0 = callback;
+
+    result->kind = 2;
+
+    result->id = id;
+
+    return result;
+
+ no_data:
+
+    g_object_unref(G_OBJECT(result));
+
+    return NULL;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : loading = traitements différés à mener.                      *
 *                status  = barre de statut à tenir informée.                  *
 *                                                                             *
@@ -435,6 +514,24 @@ static void g_elf_loading_process(GElfLoading *loading, GtkStatusStack *status)
             loading->status = (processed == loading->rel_count);
             break;
 
+        case 2:
+
+            for (iter = loading->begin; iter < loading->end; )
+            {
+                old = iter;
+
+                loading->status |= loading->callback_0(loading, format, &iter);
+
+                gtk_status_stack_update_activity_value(status, loading->id, iter - old);
+
+            }
+
+            break;
+
+        default:
+            assert(false);
+            break;
+
     }
 
 }
@@ -656,3 +753,56 @@ char *g_elf_loading_build_plt_name(const GElfLoading *loading, uint64_t index)
     return result;
 
 }
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : loading = chargement pour ELF à mener.                       *
+*                content = gestionnaire de contenu utilisé. [OUT]             *
+*                first   = première position traitée par la tâche. [OUT]      *
+*                offset  = décalage pour les données. [OUT]                   *
+*                final   = première position dans les données à exclure. [OUT]*
+*                                                                             *
+*  Description : Donne les informations utiles à la recherche de chaînes.     *
+*                                                                             *
+*  Retour      : Données brutes à analyser.                                   *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+const bin_t *g_elf_loading_get_info_for_strings(const GElfLoading *loading, GBinContent **content, phys_t *first, phys_t *offset, phys_t *final)
+{
+    const bin_t *result;                    /* Données à communiquer       */
+
+    result = loading->data;
+
+    *content = loading->content;
+    *first = loading->begin;
+    *offset = loading->global_start;
+    *final = loading->global_end;
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : loading = chargement pour ELF à poursuivre.                  *
+*                iter    = point de départ dans la zone de données traitée.   *
+*                pos     = emplacement construit à la demande. [OUT]          *
+*                                                                             *
+*  Description : Détermine l'adresse de départ d'une chaîne avec une position.*
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void g_elf_loading_compute_string_address(const GElfLoading *loading, const phys_t *iter, vmpa2t *pos)
+{
+    init_vmpa(pos, *iter, loading->global_addr + (*iter - loading->global_start));
+
+}
diff --git a/plugins/elf/loading.h b/plugins/elf/loading.h
index dcf22e2..e1407f5 100644
--- a/plugins/elf/loading.h
+++ b/plugins/elf/loading.h
@@ -67,6 +67,9 @@ GElfLoading *g_elf_loading_new_for_relocations(GElfFormat *, phys_t, phys_t, elf
 /* Crée une tâche de chargement pour ELF différée. */
 GElfLoading *g_elf_loading_new_for_applying(GElfFormat *, sym_iter_t *, phys_t, elf_rel *, size_t, phys_t, uint32_t, activity_id_t, elf_applying_cb);
 
+/* Crée une tâche de chargement de chaînes pour ELF différée. */
+GElfLoading *g_elf_loading_new_for_strings(GElfFormat *, phys_t, phys_t, phys_t, phys_t, virt_t, activity_id_t, elf_loading_cb);
+
 /* Fournit le bilan des traitements différés. */
 bool g_elf_loading_get_status(const GElfLoading *);
 
@@ -82,6 +85,12 @@ bool g_elf_loading_search_for_relocation(const GElfLoading *, const uint64_t *,
 /* Construit la désignation adaptée à un symbole importé. */
 char *g_elf_loading_build_plt_name(const GElfLoading *, uint64_t);
 
+/* Donne les informations utiles à la recherche de chaînes. */
+const bin_t *g_elf_loading_get_info_for_strings(const GElfLoading *, GBinContent **, phys_t *, phys_t *, phys_t *);
+
+/* Détermine l'adresse de départ d'une chaîne avec une position. */
+void g_elf_loading_compute_string_address(const GElfLoading *, const phys_t *, vmpa2t *);
+
 
 
 #endif  /* _PLUGINS_ELF_LOADING_H */
diff --git a/plugins/elf/strings.c b/plugins/elf/strings.c
index 6f4edac..cc40db1 100644
--- a/plugins/elf/strings.c
+++ b/plugins/elf/strings.c
@@ -27,26 +27,47 @@
 #include <assert.h>
 #include <ctype.h>
 #include <malloc.h>
-#include <string.h>
 #include <sys/param.h>
 
 
+#include <i18n.h>
+
+
 #include <arch/raw.h>
+#include <core/global.h>
+#include <core/nproc.h>
 
 
 #include "elf-int.h"
+#include "loading.h"
 #include "section.h"
 
 
 
+/**
+ * Petit complément, sur la base de :
+ * http://www.cplusplus.com/reference/cctype/
+ *
+ * On veut identifier '\t', '\f','\v','\n','\r', mais sans les caractères
+ * de contrôle, ni les espaces.
+ */
+
+#define isctrledspace(c) (isspace(c) && c != ' ')
+
+
+/* Lance les vagues de chargement des chaînes de caractères. */
+static bool parse_elf_string_data(GElfFormat *, phys_t, phys_t, virt_t, GWorkQueue *, wgroup_id_t, GtkStatusStack *, activity_id_t);
+
 /* Enregistre toutes les chaînes de caractères trouvées. */
-static bool parse_elf_string_data(GElfFormat *, phys_t, phys_t, virt_t);
+static bool do_elf_string_loading(GElfLoading *, GElfFormat *, phys_t *);
 
 
 
 /******************************************************************************
 *                                                                             *
 *  Paramètres  : format = description de l'exécutable à analyser.             *
+*                gid    = groupe de travail impliqué.                         *
+*                status = barre de statut à tenir informée.                   *
 *                                                                             *
 *  Description : Charge en mémoire toutes les chaînes trouvées.               *
 *                                                                             *
@@ -56,8 +77,10 @@ static bool parse_elf_string_data(GElfFormat *, phys_t, phys_t, virt_t);
 *                                                                             *
 ******************************************************************************/
 
-bool find_all_elf_strings(GElfFormat *format)
+bool find_all_elf_strings(GElfFormat *format, wgroup_id_t gid, GtkStatusStack *status)
 {
+    activity_id_t msg;                      /* Message de progression      */
+    GWorkQueue *queue;                      /* Gestionnaire de différés    */
     bool got_string;                        /* Indique un remplissage      */
     phys_t str_start;                       /* Début de section            */
     phys_t str_size;                        /* Taille de section           */
@@ -69,12 +92,17 @@ bool find_all_elf_strings(GElfFormat *format)
     phys_t iter;                            /* Boucle de parcours #2       */
     elf_phdr phdr;                          /* En-tête de programme ELF    */
 
+    msg = gtk_status_stack_add_activity(status, _("Finding all existing strings..."), 0);
+
+    queue = get_work_queue();
+
     got_string = false;
 
     /* Données en lecture seule */
 
     if (find_elf_section_content_by_name(format, ".rodata", &str_start, &str_size, &str_addr))
-        got_string |= parse_elf_string_data(format, str_start, str_size, str_addr);
+        got_string |= parse_elf_string_data(format, str_start, str_size, str_addr,
+                                            queue, gid, status, msg);
 
     else
     {
@@ -85,7 +113,8 @@ bool find_all_elf_strings(GElfFormat *format)
                     || (ELF_SHDR(format, sections[i], sh_flags) & SHF_STRINGS))
                 {
                     get_elf_section_content(format, &sections[i], &str_start, &str_size, &str_addr);
-                    got_string |= parse_elf_string_data(format, str_start, str_size, str_addr);
+                    got_string |= parse_elf_string_data(format, str_start, str_size, str_addr,
+                                                        queue, gid, status, msg);
                 }
 
             free(sections);
@@ -101,7 +130,8 @@ bool find_all_elf_strings(GElfFormat *format)
         for (i = 0; i < count; i++)
         {
             get_elf_section_content(format, &sections[i], &str_start, &str_size, &str_addr);
-            got_string |= parse_elf_string_data(format, str_start, str_size, str_addr);
+            got_string |= parse_elf_string_data(format, str_start, str_size, str_addr,
+                                                queue, gid, status, msg);
         }
 
         free(sections);
@@ -112,6 +142,8 @@ bool find_all_elf_strings(GElfFormat *format)
 
     if (!got_string)
     {
+        assert(false);
+
         max = ELF_HDR(format, format->header, e_phoff)
             + ELF_HDR(format, format->header, e_phnum) * ELF_SIZEOF_PHDR(format);
 
@@ -125,12 +157,15 @@ bool find_all_elf_strings(GElfFormat *format)
                 parse_elf_string_data(format,
                                       ELF_PHDR(format, phdr, p_offset),
                                       ELF_PHDR(format, phdr, p_filesz),
-                                      ELF_PHDR(format, phdr, p_vaddr));
+                                      ELF_PHDR(format, phdr, p_vaddr),
+                                      queue, gid, status, msg);
 
         }
 
     }
 
+    gtk_status_stack_remove_activity(status, msg);
+
     return true;
 
 }
@@ -142,108 +177,218 @@ bool find_all_elf_strings(GElfFormat *format)
 *                start   = début de la zone à parcourir.                      *
 *                size    = taille de l'espace à parcourir.                    *
 *                address = adresse virtuelle du début de la section.          *
+*                wq      = espace de travail dédié.                           *
+*                gid     = groupe de travail impliqué.                        *
+*                status  = barre de statut à tenir informée.                  *
+*                msg     = identifiant du message de progression.             *
+*                                                                             *
+*  Description : Lance les vagues de chargement des chaînes de caractères.    *
+*                                                                             *
+*  Retour      : true si des chaînes ont été ajoutées, ou false.              *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool parse_elf_string_data(GElfFormat *format, phys_t start, phys_t size, virt_t address, GWorkQueue *wq, wgroup_id_t gid, GtkStatusStack *status, activity_id_t msg)
+{
+    bool result;                            /* Bilan à retourner           */
+    phys_t final;                           /* Position finale à atteindre */
+    guint runs_count;                       /* Qté d'exécutions parallèles */
+    phys_t run_size;                        /* Volume réparti par exécution*/
+    guint i;                                /* Boucle de parcours          */
+    phys_t begin;                           /* Début de zone de traitement */
+    phys_t end;                             /* Fin d'un zone de traitement */
+    GElfLoading **loadings;                 /* Tâches de chargement lancées*/
+
+    result = false;
+
+    if (address > 0)
+    {
+        final = start + size;
+
+        runs_count = get_max_online_threads();
+
+        run_size = size / runs_count;
+
+        gtk_status_stack_extend_activity(status, msg, size);
+
+        loadings = (GElfLoading **)malloc(runs_count * sizeof(GElfLoading *));
+
+        for (i = 0; i < runs_count; i++)
+        {
+            begin = start + i * run_size;
+
+            if ((i + 1) == runs_count)
+                end = final;
+            else
+                end = begin + run_size;
+
+            loadings[i] = g_elf_loading_new_for_strings(format, begin, end, start, start + size, address,
+                                                        msg, do_elf_string_loading);
+
+            if (loadings[i] != NULL)
+            {
+                g_object_ref(G_OBJECT(loadings[i]));
+                g_work_queue_schedule_work(wq, G_DELAYED_WORK(loadings[i]), gid);
+            }
+
+        }
+
+        g_work_queue_wait_for_completion(wq, gid);
+
+        for (i = 0; i < runs_count; i++)
+        {
+            if (loadings[i] == NULL)
+                continue;
+
+            result |= g_elf_loading_get_status(loadings[i]);
+
+            g_object_unref(G_OBJECT(loadings[i]));
+
+        }
+
+        free(loadings);
+
+    }
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : loading = chargement de chaînes de caractères en cours.      *
+*                format  = format ELF à compléter.                            *
+*                iter    = tête de lecture évoluant avec le temps. [OUT]      *
 *                                                                             *
 *  Description : Enregistre toutes les chaînes de caractères trouvées.        *
 *                                                                             *
-*  Retour      : true si des chaînes ont été ajoutées sans erreur, ou false.  *
+*  Retour      : true si au moins une chaîne a été insérée.                   *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-static bool parse_elf_string_data(GElfFormat *format, phys_t start, phys_t size, virt_t address)
+static bool do_elf_string_loading(GElfLoading *loading, GElfFormat *format, phys_t *iter)
 {
-    bool result;                            /* Bilan à faire remonter      */
+    bool result;                            /* Bilan à retourner           */
     GBinFormat *base;                       /* Autre version du format     */
     GBinContent *content;                   /* Contenu binaire à lire      */
+    phys_t first;                           /* Première position traitée   */
+    phys_t offset;                          /* Décalage pour les données   */
+    phys_t final;                           /* Point global de fin de zone */
     const bin_t *data;                      /* Contenu complet et original */
-    vmpa2t pos;                             /* Tête de lecture             */
-    bool cut;                               /* Séparation nette ?          */
-    phys_t i;                               /* Boucle de parcours          */
     phys_t end;                             /* Position de fin de chaîne   */
+    vmpa2t pos;                             /* Tête de lecture             */
     GArchInstruction *instr;                /* Instruction décodée         */
     bool inserted;                          /* Bilan d'une insertion       */
     const mrange_t *range;                  /* Espace occupé par une chaîne*/
     GBinSymbol *symbol;                     /* Symbole à intégrer          */
+    bool cut;                               /* Coupure par étiquette ?     */
     char *label;                            /* Désignation de la chaîne    */
 
-    if (address == 0)
-        return false;
-
     result = false;
 
     /* Préparation des accès */
 
     base = G_BIN_FORMAT(format);
 
-    content = g_binary_format_get_content(base);
-
-    init_vmpa(&pos, start, address);
+    data = g_elf_loading_get_info_for_strings(loading, &content, &first, &offset, &final);
 
-    data = g_binary_content_get_raw_access(content, &pos, size);
+    /* Analyse des données */
 
-    if (data == NULL)
-        goto pesd_error;
+    if (isprint(data[*iter - offset]) || isctrledspace(data[*iter - offset]))
+    {
+        for (end = *iter; end < final; end++)
+            if (!isprint(data[end - offset])) break;
 
-    /* Boucle de parcours */
+        for (; end < final; end++)
+            if (!isctrledspace(data[end - offset])) break;
 
-    cut = true;
+        if (end < final && data[end - offset] == '\0')
+            end++;
 
-    for (i = 0; i < size; i++)
-        if (isprint(data[i]))
+        if ((first - offset) > 0 && *iter == first)
         {
-            for (end = i + 1; end < size; end++)
-                if (!isprint(data[end])) break;
+            /**
+             * Si une tâche précédente si termine par un caratère valable,
+             * elle va étendre sa chaîne jusqu'à intégrer notre chaîne initiale.
+             *
+             * Rien ne sert donc de poursuivre, on saute ici cette première chaîne.
+             */
+
+            if (isprint(data[first - 1 - offset]))
+            {
+                /**
+                 * Le seul truc, c'est que l'autre chaîne peut se terminer par isctrledspace(),
+                 * et que la notre peut commencer par le même type de caractères.
+                 *
+                 * Donc la chaîne qui déborde peut ne pas couvrir entièrement notre chaîne.
+                 *
+                 * On repositionne donc notre fin à la fin de la chaîne précédente.
+                 */
 
-            if (end < size && isspace(data[end]))
-                end++;
+                for (end = *iter; end < final; end++)
+                    if (!isprint(data[end - offset])) break;
 
-            if (end < size && data[end] == '\0')
-                end++;
+                for (; end < final; end++)
+                    if (!isctrledspace(data[end - offset])) break;
 
-            init_vmpa(&pos, start + i, address + i);
+                goto skip_first;
 
-            instr = g_raw_instruction_new_array(content, MDS_8_BITS, end - i, &pos, format->endian);
-            assert(instr != NULL);
+            }
 
-            g_raw_instruction_mark_as_string(G_RAW_INSTRUCTION(instr), true);
+        }
 
-            inserted = g_preload_info_add_instruction(base->info, instr);
+        g_elf_loading_compute_string_address(loading, iter, &pos);
 
-            if (inserted)
-            {
-                range = g_arch_instruction_get_range(instr);
+        instr = g_raw_instruction_new_array(content, MDS_8_BITS, end - *iter, &pos, format->endian);
+        assert(instr != NULL);
 
-                symbol = g_binary_symbol_new(range, STP_RO_STRING);
-                g_binary_format_add_symbol(base, symbol);
+        g_raw_instruction_mark_as_string(G_RAW_INSTRUCTION(instr), true);
 
-                /* Jointure avec la chaîne précédente ? */
+        inserted = g_preload_info_add_instruction(base->info, instr);
 
-                if (cut)
-                {
-                    label = create_string_label(base, get_mrange_addr(range), end - i);
+        result |= inserted;
 
-                    g_binary_symbol_set_alt_label(symbol, label);
+        if (inserted)
+        {
+            range = g_arch_instruction_get_range(instr);
 
-                    free(label);
+            symbol = g_binary_symbol_new(range, STP_RO_STRING);
+            g_binary_format_add_symbol(base, symbol);
 
-                }
+            /* Jointure avec la chaîne précédente ? */
 
-            }
+            if ((first - offset) == 0)
+                cut = true;
+            else
+                cut = (data[*iter - offset - 1] == '\0');
 
-            /* Conclusion */
+            if (cut)
+            {
+                label = create_string_label(base, get_mrange_addr(range), end - *iter);
 
-            cut = (data[end - 1] == '\0');
+                g_binary_symbol_set_alt_label(symbol, label);
 
-            i = end - 1;
-            result = true;
+                free(label);
+
+            }
 
         }
-        else cut = true;
 
- pesd_error:
+        /* Conclusion */
+
+ skip_first:
 
-    g_object_unref(G_OBJECT(content));
+        *iter = end;
+
+    }
+
+    else
+        (*iter)++;
 
     return result;
 
diff --git a/plugins/elf/strings.h b/plugins/elf/strings.h
index 0dfe11e..8fc2b3e 100644
--- a/plugins/elf/strings.h
+++ b/plugins/elf/strings.h
@@ -28,9 +28,13 @@
 #include "format.h"
 
 
+#include <glibext/delayed.h>
+#include <gtkext/gtkstatusstack.h>
 
-/*Charge en mémoire toutes les chaînes trouvées. */
-bool find_all_elf_strings(GElfFormat *);
+
+
+/* Charge en mémoire toutes les chaînes trouvées. */
+bool find_all_elf_strings(GElfFormat *, wgroup_id_t , GtkStatusStack *);
 
 
 
diff --git a/plugins/elf/symbols.h b/plugins/elf/symbols.h
index f51e504..1b963da 100644
--- a/plugins/elf/symbols.h
+++ b/plugins/elf/symbols.h
@@ -28,7 +28,6 @@
 #include "format.h"
 
 
-#include <arch/processor.h>
 #include <glibext/delayed.h>
 #include <gtkext/gtkstatusstack.h>
 
-- 
cgit v0.11.2-87-g4458