From c1ca03be00a4e975f89d30edfb72b57fb5612282 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Mon, 27 Jan 2020 19:48:58 +0100
Subject: Created a huge optimization for the Dex format loading.

---
 plugins/dex/class.c | 143 +++++++++++++++++++++++++++++++++++-----------------
 plugins/dex/class.h |   4 +-
 plugins/dex/pool.c  |  45 +++++++++++++----
 src/common/sort.c   |  35 +++++++++++++
 src/common/sort.h   |   3 ++
 src/format/format.c | 107 +++++++++++++++++++++++++++++++++++++++
 src/format/format.h |   3 ++
 7 files changed, 282 insertions(+), 58 deletions(-)

diff --git a/plugins/dex/class.c b/plugins/dex/class.c
index dafa984..d549014 100644
--- a/plugins/dex/class.c
+++ b/plugins/dex/class.c
@@ -222,11 +222,7 @@ GDexClass *g_dex_class_new(GDexFormat *format, const class_def_item *def)
     uleb128_t index;                        /* Conservation du dernier id  */
     uleb128_t i;                            /* Boucle de parcours          */
     GDexField *field;                       /* Champ chargé                */
-    GDexPool *pool;                         /* Table de ressources         */
-    GDataType *ctype;                       /* Type créé par la classe     */
-    GBinFormat *base;                       /* Autre version du format     */
     GDexMethod *method;                     /* Méthode chargée             */
-    GBinRoutine *routine;                   /* Version interne de méthode  */
 
     result = g_object_new(G_TYPE_DEX_CLASS, NULL);
 
@@ -292,16 +288,6 @@ GDexClass *g_dex_class_new(GDexFormat *format, const class_def_item *def)
      * Chargement des méthodes de classe.
      */
 
-    pool = g_dex_format_get_pool(format);
-
-    ctype = g_dex_pool_get_type_(pool, def->class_idx);
-
-    g_object_unref(G_OBJECT(pool));
-
-    if (ctype == NULL) goto gdcn_unknown_type;
-
-    base = G_BIN_FORMAT(format);
-
     index = 0;
 
     result->dmethods_count = data.direct_methods_size;
@@ -314,18 +300,6 @@ GDexClass *g_dex_class_new(GDexFormat *format, const class_def_item *def)
 
         result->direct_methods[i] = method;
 
-        /* Ajout à la liste des symboles */
-        if (g_dex_method_has_dex_body(method))
-        {
-            routine = g_dex_method_get_routine(method);
-
-            g_object_ref(G_OBJECT(ctype));
-            g_binary_routine_set_namespace(routine, ctype, strdup("."));
-
-            g_binary_format_add_symbol(base, G_BIN_SYMBOL(routine));
-
-        }
-
     }
 
     index = 0;
@@ -340,32 +314,14 @@ GDexClass *g_dex_class_new(GDexFormat *format, const class_def_item *def)
 
         result->virtual_methods[i] = method;
 
-        /* Ajout à la liste des symboles */
-        if (g_dex_method_has_dex_body(method))
-        {
-            routine = g_dex_method_get_routine(method);
-
-            g_object_ref(G_OBJECT(ctype));
-            g_binary_routine_set_namespace(routine, ctype, strdup("."));
-
-            g_binary_format_add_symbol(base, G_BIN_SYMBOL(routine));
-
-        }
-
     }
 
-    g_object_unref(G_OBJECT(ctype));
-
  gdcn_done:
 
     return result;
 
  gdcn_bad_method:
 
-    g_object_unref(G_OBJECT(ctype));
-
- gdcn_unknown_type:
-
  gdcn_bad_field:
 
  gdcn_bad_item:
@@ -379,7 +335,7 @@ GDexClass *g_dex_class_new(GDexFormat *format, const class_def_item *def)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : class   = informations chargées à consulter.                 *
+*  Paramètres  : class = informations chargées à consulter.                   *
 *                                                                             *
 *  Description : Fournit la définition brute d'une classe.                    *
 *                                                                             *
@@ -398,7 +354,7 @@ const class_def_item *g_dex_class_get_definition(const GDexClass *class)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : class   = informations chargées à consulter.                 *
+*  Paramètres  : class = informations chargées à consulter.                   *
 *                                                                             *
 *  Description : Fournit la définition brute des données d'une classe.        *
 *                                                                             *
@@ -660,6 +616,101 @@ GDexMethod *g_dex_class_get_method(const GDexClass *class, bool virtual, size_t
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : class   = informations chargées à consulter.                 *
+*                symbols = liste de symboles complétée. [OUT]                 *
+*                count   = taille de cette liste. [OUT]                       *
+*                                                                             *
+*  Description : Etablit une liste de tous les symboles d'une classe.         *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool g_dex_class_get_collect_symbols(const GDexClass *class, GBinSymbol ***symbols, size_t *count)
+{
+    bool result;                            /* Bilan à retourner           */
+    GDexPool *pool;                         /* Table de ressources         */
+    GDataType *ctype;                       /* Type créé par la classe     */
+    size_t slots_used;                      /* Compteur d'utilisations     */
+    size_t i;                               /* Boucle de parcours          */
+    GDexMethod *method;                     /* Méthode chargée             */
+    GBinRoutine *routine;                   /* Version interne de méthode  */
+
+    result = false;
+
+    /* Contexte des méthodes */
+
+    pool = g_dex_format_get_pool(class->format);
+
+    ctype = g_dex_pool_get_type_(pool, class->definition.class_idx);
+
+    g_object_unref(G_OBJECT(pool));
+
+    if (ctype == NULL) goto unknown_type;
+
+    /* Intégration des méthodes */
+
+    *symbols = realloc(*symbols, (*count + class->dmethods_count + class->vmethods_count) * sizeof(GBinSymbol *));
+
+    result = true;
+
+    slots_used = 0;
+
+    for (i = 0; i < class->dmethods_count; i++)
+    {
+        method = class->direct_methods[i];
+
+        if (g_dex_method_has_dex_body(method))
+        {
+            routine = g_dex_method_get_routine(method);
+
+            g_object_ref(G_OBJECT(ctype));
+            g_binary_routine_set_namespace(routine, ctype, strdup("."));
+
+            (*symbols)[*count + slots_used] = G_BIN_SYMBOL(routine);
+            slots_used++;
+
+        }
+
+    }
+
+    *count += slots_used;
+
+    slots_used = 0;
+
+    for (i = 0; i < class->vmethods_count; i++)
+    {
+        method = class->virtual_methods[i];
+
+        if (g_dex_method_has_dex_body(method))
+        {
+            routine = g_dex_method_get_routine(method);
+
+            g_object_ref(G_OBJECT(ctype));
+            g_binary_routine_set_namespace(routine, ctype, strdup("."));
+
+            (*symbols)[*count + slots_used] = G_BIN_SYMBOL(routine);
+            slots_used++;
+
+        }
+
+    }
+
+    *count += slots_used;
+
+    g_object_unref(G_OBJECT(ctype));
+
+ unknown_type:
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : class  = informations chargées à consulter.                  *
 *                format = format permettant d'obtenir une adresse complète.   *
 *                                                                             *
diff --git a/plugins/dex/class.h b/plugins/dex/class.h
index cae4a5a..a7874a7 100644
--- a/plugins/dex/class.h
+++ b/plugins/dex/class.h
@@ -50,7 +50,6 @@ typedef struct _GDexClass GDexClass;
 typedef struct _GDexClassClass GDexClassClass;
 
 
-
 /* Détermine le type d'une classe issue du code source. */
 GType g_dex_class_get_type(void);
 
@@ -84,6 +83,9 @@ size_t g_dex_class_count_methods(const GDexClass *, bool);
 /* Fournit une méthode chargée correspondant à un type donné. */
 GDexMethod *g_dex_class_get_method(const GDexClass *, bool, size_t);
 
+/* Etablit une liste de tous les symboles d'une classe. */
+bool g_dex_class_get_collect_symbols(const GDexClass *, GBinSymbol ***, size_t *);
+
 /* Intègre la méthode en tant que portion de code. */
 void g_dex_class_include_as_portion(const GDexClass *, GExeFormat *);
 
diff --git a/plugins/dex/pool.c b/plugins/dex/pool.c
index c24617a..d800e8f 100644
--- a/plugins/dex/pool.c
+++ b/plugins/dex/pool.c
@@ -251,6 +251,11 @@ bool g_dex_pool_load_all_string_symbols(GDexPool *pool, wgroup_id_t gid, GtkStat
 
     gtk_status_stack_remove_activity(status, msg);
 
+    /* Insertion en tant que symboles */
+
+    if (result)
+        result = g_binary_format_add_symbols(G_BIN_FORMAT(pool->format), pool->strings, count);
+
     return result;
 
 }
@@ -367,7 +372,6 @@ GBinSymbol *g_dex_pool_get_string_symbol(GDexPool *pool, uint32_t index)
     const char *string;                     /* Chaîne de caractères liée   */
     GBinFormat *base;                       /* Autre version du format     */
     GBinSymbol *new;                        /* Nouveau symbol créé         */
-    bool inserted;                          /* Bilan d'une insertion       */
 
     result = NULL;
 
@@ -385,16 +389,10 @@ GBinSymbol *g_dex_pool_get_string_symbol(GDexPool *pool, uint32_t index)
 
         new = g_string_symbol_new_read_only(base, &range, SET_MUTF_8);
 
-        g_string_symbol_build_label(G_STR_SYMBOL(new), base);
-
-        g_object_ref(G_OBJECT(new));
-        inserted = g_binary_format_add_symbol(base, new);
-
-        if (inserted)
-            pool->strings[index] = new;
+        if (new != NULL)
+            g_string_symbol_build_label(G_STR_SYMBOL(new), base);
 
-        else
-            g_object_unref(G_OBJECT(new));
+        pool->strings[index] = new;
 
     }
 
@@ -1175,10 +1173,14 @@ bool g_dex_pool_load_all_classes(GDexPool *pool, wgroup_id_t gid, GtkStatusStack
     uint32_t run_size;                      /* Volume réparti par exécution*/
     GWorkQueue *queue;                      /* Gestionnaire de différés    */
     activity_id_t msg;                      /* Message de progression      */
-    guint i;                                /* Boucle de parcours          */
+    guint i;                                /* Boucle de parcours #1       */
     uint32_t begin;                         /* Début de bloc de traitement */
     uint32_t end;                           /* Fin d'un bloc de traitement */
     GDexLoading *loading;                   /* Tâche de chargement à lancer*/
+    GBinSymbol **symbols;                   /* Symboles présents à injecter*/
+    size_t scount;                          /* Quantité de ces symboles    */
+    uint32_t j;                             /* Boucle de parcours #2       */
+    size_t k;                               /* Boucle de parcours #3       */
 
     result = true;
 
@@ -1216,6 +1218,27 @@ bool g_dex_pool_load_all_classes(GDexPool *pool, wgroup_id_t gid, GtkStatusStack
 
     gtk_status_stack_remove_activity(status, msg);
 
+    /* Insertion en tant que symboles */
+
+    if (result)
+    {
+        symbols = NULL;
+        scount = 0;
+
+        for (j = 0; j < count && result; j++)
+            result = g_dex_class_get_collect_symbols(pool->classes[j], &symbols, &scount);
+
+        if (result)
+            result = g_binary_format_add_symbols(G_BIN_FORMAT(pool->format), symbols, count);
+
+        for (k = 0; k < scount; k++)
+            g_object_unref(symbols[k]);
+
+        if (symbols != NULL)
+            free(symbols);
+
+    }
+
     return result;
 
 }
diff --git a/src/common/sort.c b/src/common/sort.c
index e34d995..a905440 100644
--- a/src/common/sort.c
+++ b/src/common/sort.c
@@ -267,6 +267,41 @@ void *_qinsert(void *base, size_t *nmemb, size_t size, void *new, size_t index)
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : base  = adresse du tableau à parcourir.                      *
+*                nmemb = nombre d'éléments présents au total. [OUT]           *
+*                size  = taille de chaque élément du tableau.                 *
+*                new   = nouveaux éléments à insérer.                         *
+*                count = quantité de ces nouveaux éléments.                   *
+*                index = indice du point d'insertion.                         *
+*                                                                             *
+*  Description : Ajoute à l'endroit indiqué des éléments dans un tableau.     *
+*                                                                             *
+*  Retour      : Nouvel emplacement du tableau agrandi.                       *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void *_qinsert_batch(void *base, size_t *nmemb, size_t size, void *new, size_t count, size_t index)
+{
+    void *result;                           /* Tableau trié à retourner    */
+
+    result = realloc(base, (*nmemb + count) * size);
+
+    if (index < *nmemb)
+        memmove((char *)result + (index + count) * size, (char *)result + index * size, (*nmemb - index) * size);
+
+    (*nmemb) += count;
+
+    memcpy((char *)result + index * size, new, count * size);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : base   = adresse du tableau à parcourir.                     *
 *                nmemb  = nombre d'éléments présents au total. [OUT]          *
 *                size   = taille de chaque élément du tableau.                *
diff --git a/src/common/sort.h b/src/common/sort.h
index eb86ccb..7362f64 100644
--- a/src/common/sort.h
+++ b/src/common/sort.h
@@ -49,6 +49,9 @@ bool bsearch_index(const void *, const void *, size_t, size_t, __compar_fn_t, si
 /* Ajoute à l'endroit indiqué un élément dans un tableau. */
 void *_qinsert(void *, size_t *, size_t, void *, size_t);
 
+/* Ajoute à l'endroit indiqué des éléments dans un tableau. */
+void *_qinsert_batch(void *, size_t *, size_t, void *, size_t, size_t);
+
 /* Ajoute au bon endroit un élément dans un tableau trié. */
 void *qinsert(void *, size_t *, size_t, __compar_fn_t, void *);
 
diff --git a/src/format/format.c b/src/format/format.c
index ab4864f..f6a4bd0 100644
--- a/src/format/format.c
+++ b/src/format/format.c
@@ -871,6 +871,113 @@ bool g_binary_format_add_symbol(GBinFormat *format, GBinSymbol *symbol)
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : format  = informations chargées à compléter.                 *
+*                symbols = ensemble de symboles à ajouter à la liste.         *
+*                count   = taille de cet ensemble.                            *
+*                                                                             *
+*  Description : Ajoute plusieurs symboles à la collection du format binaire. *
+*                                                                             *
+*  Retour      : true si les symboles dûment localisés ont été insérés.       *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool g_binary_format_add_symbols(GBinFormat *format, GBinSymbol **symbols, size_t count)
+{
+    bool result;                            /* Statut d'ajout à retourner  */
+#ifndef NDEBUG
+    phys_t last;                            /* Dernière position rencontrée*/
+#endif
+    size_t i;                               /* Boucle de parcours          */
+#ifndef NDEBUG
+    const mrange_t *range;                  /* Couverture du symbole       */
+    const vmpa2t *addr;                     /* Emplacement du symbole      */
+#endif
+    size_t index;                           /* Indice du point d'insertion */
+
+    /**
+     * Pour que les fonctions de recherche basées sur _g_binary_format_find_symbol()
+     * fassent bien leur office, il faut que les symboles soient triés.
+     *
+     * Cependant, les localisations à satisfaire lors d'une recherche recontrent
+     * un problème si les positions physiques ne sont pas renseignées. En effet
+     * les adresses virtuelles en sont potentiellement décorrélées (c'est le cas
+     * avec le format ELF par exemple, où les zones en mémoire ne suivent pas le
+     * même ordre que les segments du binaire).
+     *
+     * Comme les comparaisons entre localisations se réalisent sur les éléments
+     * renseignés communs, à commencer par la position physique si c'est possible,
+     * une localisation s'appuyant uniquement sur une adresse virtuelle va être
+     * analysée suivant une liste non triée d'adresses virtuelles.
+     *
+     * On corrige donc le tir si besoin est en forçant la comparaison via les
+     * positions physiques.
+     */
+
+#ifndef NDEBUG
+    last = VMPA_NO_PHYSICAL;
+
+    for (i = 0; i < count; i++)
+    {
+        range = g_binary_symbol_get_range(symbols[i]);
+        addr = get_mrange_addr(range);
+
+        assert(has_phys_addr(addr)
+               || g_binary_symbol_get_status(symbols[i]) == SSS_IMPORTED
+               || g_binary_symbol_get_status(symbols[i]) == SSS_DYNAMIC);
+
+
+        if (has_phys_addr(addr))
+        {
+            assert(last == VMPA_NO_PHYSICAL || last <= get_phy_addr(addr));
+            last = get_phy_addr(addr);
+        }
+
+    }
+#endif
+
+    g_binary_format_lock_unlock_symbols_wr(format, true);
+
+    /**
+     * Avec tous les traitements parallèles, il est possible que plusieurs chemins d'exécution
+     * amènent à la création d'un même symbole.
+     *
+     * Plutôt que de verrouiller la liste des symboles en amont (et donc assez longtemps)
+     * pour faire une vérification avant construction puis ajout, on préfère limiter
+     * l'état figé à cette seule fonction, quitte à annuler le travail fourni pour la
+     * construction du symbole dans les cas peu fréquents où le symbole était déjà en place.
+     */
+
+    result = bsearch_index(symbols[0], format->symbols, format->sym_count,
+                           sizeof(GBinSymbol *), (__compar_fn_t)g_binary_symbol_cmp, &index);
+
+    if (!result)
+    {
+        for (i = 0; i < count; i++)
+            g_object_ref(G_OBJECT(symbols[i]));
+
+        format->symbols = _qinsert_batch(format->symbols, &format->sym_count,
+                                         sizeof(GBinSymbol *), symbols, count, index);
+
+        format->sym_stamp++;
+        result = true;
+
+    }
+
+    g_binary_format_lock_unlock_symbols_wr(format, false);
+
+    if (result)
+        for (i = 0; i < count; i++)
+            g_signal_emit_by_name(format, "symbol-added", symbols[i]);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : format = informations chargées à compléter.                  *
 *                index  = indice du symbole à retirer de la liste.            *
 *                                                                             *
diff --git a/src/format/format.h b/src/format/format.h
index 748fd54..b001a15 100644
--- a/src/format/format.h
+++ b/src/format/format.h
@@ -129,6 +129,9 @@ GBinSymbol *g_binary_format_get_symbol(const GBinFormat *, size_t);
 /* Ajoute un symbole à la collection du format binaire. */
 bool g_binary_format_add_symbol(GBinFormat *, GBinSymbol *);
 
+/* Ajoute plusieurs symboles à la collection du format binaire. */
+bool g_binary_format_add_symbols(GBinFormat *, GBinSymbol **, size_t);
+
 /* Retire un symbole de la collection du format binaire. */
 void g_binary_format_remove_symbol(GBinFormat *, GBinSymbol *);
 
-- 
cgit v0.11.2-87-g4458