From c1ca03be00a4e975f89d30edfb72b57fb5612282 Mon Sep 17 00:00:00 2001 From: Cyrille Bagard 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