diff options
-rw-r--r-- | src/analysis/disass/area.c | 844 | ||||
-rw-r--r-- | src/analysis/disass/area.h | 6 |
2 files changed, 544 insertions, 306 deletions
diff --git a/src/analysis/disass/area.c b/src/analysis/disass/area.c index 339b4ad..5e89daa 100644 --- a/src/analysis/disass/area.c +++ b/src/analysis/disass/area.c @@ -63,6 +63,7 @@ typedef struct _mem_area GArchInstruction **instructions; /* Instructions en place */ size_t count; /* Quantité d'instructions */ GMutex mutex; /* Garantie d'atomicité */ + GMutex *global; /* Atomicité sur zones multi. */ bool is_exec; /* Zone exécutable ? */ @@ -70,22 +71,25 @@ typedef struct _mem_area /* Initialise une aire de données à partir d'une adresse donnée. */ -static void init_mem_area_from_addr(mem_area *, const vmpa2t *, phys_t, const GLoadedBinary *); +static void init_mem_area_from_addr(mem_area *, const vmpa2t *, phys_t, const GLoadedBinary *, GMutex *); /* Libère d'une aire de données les ressources allouées. */ static void fini_mem_area(mem_area *); /* Indique si une zone donnée est intégralement vierge. */ +static bool _is_range_empty_in_mem_area(mem_area *, phys_t, phys_t); + +/* Indique si une zone donnée est intégralement vierge. */ static bool is_range_empty_in_mem_area(mem_area *, phys_t, phys_t); /* Indique si une zone donnée est intégralement occupée. */ static bool is_range_busy_in_mem_area(mem_area *, phys_t, phys_t); /* Marque une série d'octets comme ayant été traités. */ -static bool mark_range_in_mem_area_as_processed(mem_area *, GArchInstruction *, bool, bool); +static void mark_range_in_mem_area_as_processed(mem_area *area, GArchInstruction *, phys_t, phys_t); -/* Marque une série d'octets comme ayant été traités. */ -static void mark_range_in_mem_area_as_overlapping(mem_area *, phys_t); +/* Marque une série d'octets comme non traités. */ +static void unmark_range_in_mem_area_as_processed(mem_area *, phys_t, phys_t); /* Crée une instruction issue d'un désassemblage brut. */ static GArchInstruction *load_raw_instruction_from_mem_area(mem_area *, phys_t, vmpa2t *, phys_t *); @@ -102,8 +106,17 @@ static void fill_mem_area(mem_area *, mem_area *, size_t, GProcContext *, GtkSta /* Rassemble les instructions conservées dans une zone donnée. */ static GArchInstruction **get_instructions_from_mem_area(const mem_area *, GArchInstruction **, size_t *); -/* Insère une instruction dans un découpage en aires. */ -static void insert_extra_instr_into_mem_areas(mem_area *, size_t, GArchInstruction *); + + +/* -------------------------- TRAITEMENT DE ZONES PAR LOTS -------------------------- */ + + +/* Insère une instruction dans un ensemble d'aires. */ +static bool insert_instr_into_mem_areas(mem_area *, size_t, GArchInstruction *, mem_area **); + +/* Force l'insertion d'une instruction dans un ensemble d'aires. */ +static void insert_instr_into_mem_areas_forced(mem_area *, size_t, GArchInstruction *); + /* ----------------------- MANIPULATIONS PARALLELES DES ZONES ----------------------- */ @@ -134,6 +147,7 @@ typedef struct _GAreaCollector size_t created; /* Nombre de zones créées */ GLoadedBinary *binary; /* Binaire à associer aux zones*/ + GMutex *global; /* Verrou pour zones multi. */ phys_t first; /* Début de traitement */ phys_t last; /* Fin de traitement */ @@ -194,7 +208,7 @@ static void g_area_collector_finalize(GAreaCollector *); static void g_area_collector_process(GAreaCollector *, GtkStatusStack *); /* Crée une tâche de calcul des zones binaires à désassembler. */ -static GAreaCollector *g_area_collector_new_intro(activity_id_t, GLoadedBinary *, phys_t, phys_t, bool); +static GAreaCollector *g_area_collector_new_intro(activity_id_t, GLoadedBinary *, GMutex *, phys_t, phys_t, bool); /* Construit une liste bornée de zones contigües. */ static void g_area_collector_do_compute(GAreaCollector *, GtkStatusStack *); @@ -224,6 +238,7 @@ static void g_area_collector_do_collect(GAreaCollector *, GtkStatusStack *); * addr = adresse de départ de l'espace à mettre en place. * * len = longueur de l'espace à créer. * * binary = binaire analysé content quantités d'informations. * +* global = verrou pour les accès sur plusieurs zones. * * * * Description : Initialise une aire de données à partir d'une adresse donnée.* * * @@ -233,7 +248,7 @@ static void g_area_collector_do_collect(GAreaCollector *, GtkStatusStack *); * * ******************************************************************************/ -static void init_mem_area_from_addr(mem_area *area, const vmpa2t *addr, phys_t len, const GLoadedBinary *binary) +static void init_mem_area_from_addr(mem_area *area, const vmpa2t *addr, phys_t len, const GLoadedBinary *binary, GMutex *global) { GBinContent *content; /* Données binaires à lire */ @@ -257,6 +272,8 @@ static void init_mem_area_from_addr(mem_area *area, const vmpa2t *addr, phys_t l area->count = 0; g_mutex_init(&area->mutex); + area->global = global; + } @@ -310,15 +327,42 @@ static void fini_mem_area(mem_area *area) * * ******************************************************************************/ -static bool is_range_empty_in_mem_area(mem_area *area, phys_t start, phys_t len) +static bool _is_range_empty_in_mem_area(mem_area *area, phys_t start, phys_t len) { bool result; /* Résultat à renvoyer */ + assert(!g_mutex_trylock(&area->mutex)); + assert((start + len) <= get_mrange_length(&area->range)); + result = test_none_in_bit_field(area->processed, start, len); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : area = aire représentant à contenu à parcourir. * +* start = début de la zone à manipuler. * +* len = taille de cette même aire de données. * +* * +* Description : Indique si une zone donnée est intégralement vierge. * +* * +* Retour : true si l'aire visée n'a jamais été traitée, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool is_range_empty_in_mem_area(mem_area *area, phys_t start, phys_t len) +{ + bool result; /* Résultat à renvoyer */ + /** * Les accès au champ de bits sont atomiques, mais la fonction - * mark_range_in_mem_area_as_processed() peut y accéder en deux temps + * (un)mark_range_in_mem_area_as_processed() peut y accéder en deux temps * (réinitialisation, puis définition). * * On protège donc les accès de façon constante. @@ -326,7 +370,7 @@ static bool is_range_empty_in_mem_area(mem_area *area, phys_t start, phys_t len) g_mutex_lock(&area->mutex); - result = test_none_in_bit_field(area->processed, start, len); + result = _is_range_empty_in_mem_area(area, start, len); g_mutex_unlock(&area->mutex); @@ -357,7 +401,7 @@ static bool is_range_busy_in_mem_area(mem_area *area, phys_t start, phys_t len) /** * Les accès au champ de bits sont atomiques, mais la fonction - * mark_range_in_mem_area_as_processed() peut y accéder en deux temps + * (un)mark_range_in_mem_area_as_processed() peut y accéder en deux temps * (réinitialisation, puis définition). * * On protège donc les accès de façon constante. @@ -376,170 +420,58 @@ static bool is_range_busy_in_mem_area(mem_area *area, phys_t start, phys_t len) /****************************************************************************** * * -* Paramètres : area = aire représentant à contenu à parcourir. * -* instr = instruction à mémoriser pour la suite. * -* force = impose l'enregistrement de l'instruction. * -* overlap = indique si l'instruction peut déborder de la zone. * +* Paramètres : area = aire représentant à contenu à parcourir. * +* instr = instruction à mémoriser pour la suite ou NULL. * +* start = début de la zone à manipuler. * +* len = taille de cette même aire de données. * * * * Description : Marque une série d'octets comme ayant été traités. * * * -* Retour : true si l'enregistrement a bien été réalisé, false sinon. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static bool mark_range_in_mem_area_as_processed(mem_area *area, GArchInstruction *instr, bool force, bool overlap) +static void mark_range_in_mem_area_as_processed(mem_area *area, GArchInstruction *instr, phys_t start, phys_t len) { - bool result; /* Bilan d'action à renvoyer */ - const vmpa2t *start; /* Adresse de départ de la zone*/ - const mrange_t *range; /* Emplacement d'instruction */ - const vmpa2t *addr; /* Début de la zone à traiter */ - phys_t len; /* Taille de l'aire visée */ - phys_t offset; /* Décalage de départ */ - phys_t coverage_start; /* Début de zone à vider */ - phys_t coverage_len; /* Taille de cette même zone */ - phys_t i; /* Boucle de parcours */ - GArchInstruction *old; /* Instruction remplacée */ - const mrange_t *old_range; /* Emplacement de l'instruction*/ - phys_t old_len; /* Taille de cette instruction */ - - /* Détermination de la couverture */ - - start = get_mrange_addr(&area->range); - - range = g_arch_instruction_get_range(instr); - addr = get_mrange_addr(range); - len = get_mrange_length(range); - - offset = compute_vmpa_diff(start, addr); - - /** - * On vérifie que l'instruction tient toute entière dans la zone courante. - * - * C'est normalement forcément le cas, puisque les accès aux données lues - * pour la mise en place d'une instruction sont restreintes à la zone couverte. - * - * Cependant ce n'est pas forcément le cas lors de l'injection des - * instructions préchargées. En effet, une chaîne de caractères peut être - * découpée sur plusieurs zones : il arrive par exemple que le segment BSS - * englobe une partie de la section des chaînes. Cette section est donc coupée - * en deux portions, ce qui conduit à la création de deux zones distinctes. - * - * Dans ce cas, on inscrit ici l'instruction résultante, et charge à - * l'appelant de marquer la zone suivante comme déjà partiellement traitée. - * - * Cette situation est représentée par le test 'overlapping_areas.py'. - */ - - if ((offset + len) > get_mrange_length(&area->range)) - { - if (overlap) - len = get_mrange_length(&area->range) - offset; - #ifndef NDEBUG - else - assert(false); + bool status; /* Validation de disponibilité */ + phys_t i; /* Boucle de parcours */ #endif - } - - /* Début des choses sérieuses */ - - g_mutex_lock(&area->mutex); - - /* Vérification de couverture */ - - result = test_none_in_bit_field(area->processed, offset, len); - - if (!result) - { - if (!force) goto mrimaap_exit; - - /** - * Un cas de remplacement forcé intervient en ARM, lorsque qu'une - * instruction utilise une valeur immédiate placée dans le code. - * - * Cette valeur doit être référencée en tant que donnée. - * - * Mais cette même valeur a pu être désassemblée en tant que code - * exécutable si le flot d'exécution s'est poursuivi jusqu'à elle. - * - * C'est par exemple le cas lors de l'utilisation d'appels système - * en assembleur, qui ne sont pas reconnus en tant qu'instructions - * cassant le flot d'exécution (typiquement : un exit()). - * - * On réinitialise donc la zone couverte par la nouvelle instruction. - */ - - coverage_start = offset; - coverage_len = len; - - /** - * Par ailleurs, il se peut que la nouvelle instruction ne couvre - * que partiellement une instruction existante. - * - * Il faut donc dans ce cas remonter la table des enregistrements - * pour retrouver l'instruction à l'origine de la couverture à remplacer. - */ - - while (area->instructions[coverage_start] == NULL) - { - if (coverage_start == 0) - break; + assert(!g_mutex_trylock(&area->mutex)); - coverage_start--; - coverage_len++; - - } - - assert(area->instructions[coverage_start] != NULL); - - for (i = 0; i < coverage_len; i++) - { - old = area->instructions[coverage_start + i]; + assert((start + len) <= get_mrange_length(&area->range)); - if (old != NULL) - { - old_range = g_arch_instruction_get_range(old); - old_len = get_mrange_length(old_range); + assert(instr != NULL || start == 0); - reset_in_bit_field(area->processed, coverage_start + i, old_len); + /* Application dans le registre des bits */ - g_object_unref(G_OBJECT(old)); - area->instructions[coverage_start + i] = NULL; +#ifndef NDEBUG - g_atomic_pointer_add(&area->count, -1); + status = test_none_in_bit_field(area->processed, start, len); - } + assert(status); - } +#endif - result = true; - - } + set_in_bit_field(area->processed, start, len); /* Inscription de l'instruction dans les comptes */ #ifndef NDEBUG for (i = 0; i < len; i++) - assert(area->instructions[offset + i] == NULL); + assert(area->instructions[start + i] == NULL); #endif - set_in_bit_field(area->processed, offset, len); - - area->instructions[offset] = instr; - g_atomic_pointer_add(&area->count, 1); - - mrimaap_exit: - - /* Fin des choses sérieuses */ - - g_mutex_unlock(&area->mutex); - - return result; + if (instr != NULL) + { + area->instructions[start] = instr; + g_atomic_pointer_add(&area->count, 1); + } } @@ -547,9 +479,11 @@ static bool mark_range_in_mem_area_as_processed(mem_area *area, GArchInstruction /****************************************************************************** * * * Paramètres : area = aire représentant à contenu à parcourir. * -* count = nombre d'octets à considérer comme traités. * +* instr = instruction à mémoriser pour la suite ou NULL. * +* start = début de la zone à manipuler. * +* len = taille de cette même aire de données. * * * -* Description : Marque une série d'octets comme ayant été traités. * +* Description : Marque une série d'octets comme non traités. * * * * Retour : - * * * @@ -557,42 +491,35 @@ static bool mark_range_in_mem_area_as_processed(mem_area *area, GArchInstruction * * ******************************************************************************/ -static void mark_range_in_mem_area_as_overlapping(mem_area *area, phys_t count) +static void unmark_range_in_mem_area_as_processed(mem_area *area, phys_t start, phys_t len) { - bool status; /* Présence d'instructions ? */ phys_t i; /* Boucle de parcours */ GArchInstruction *old; /* Instruction remplacée */ - const mrange_t *old_range; /* Emplacement de l'instruction*/ - phys_t old_len; /* Taille de cette instruction */ - assert(count <= get_mrange_length(&area->range)); + assert(!g_mutex_trylock(&area->mutex)); - g_mutex_lock(&area->mutex); - - status = test_none_in_bit_field(area->processed, 0, count); + assert((start + len) <= get_mrange_length(&area->range)); - if (!status) - for (i = 0; i < count; i++) - { - old = area->instructions[i]; + /* Retrait d'éventuelles instructions */ - if (old != NULL) - { - old_range = g_arch_instruction_get_range(old); - old_len = get_mrange_length(old_range); + for (i = 0; i < len; i++) + { + old = area->instructions[start + i]; - reset_in_bit_field(area->processed, i, old_len); + if (old != NULL) + { + g_object_unref(G_OBJECT(old)); + area->instructions[start + i] = NULL; - g_object_unref(G_OBJECT(old)); - area->instructions[i] = NULL; + g_atomic_pointer_add(&area->count, -1); - g_atomic_pointer_add(&area->count, -1); + } - } + } - } + /* Actualisation du registre des bits */ - g_mutex_unlock(&area->mutex); + reset_in_bit_field(area->processed, start, len); } @@ -752,39 +679,21 @@ static void update_address_as_routine(GBinFormat *format, const vmpa2t *addr) void load_code_from_mem_area(mem_area *area, mem_area *list, size_t count, GProcContext *ctx, const vmpa2t *start, bool force, GtkStatusStack *status, activity_id_t id) { - - - GBinFormat *format; /* Format du fichier binaire */ GArchProcessor *proc; /* Architecture du binaire */ GBinContent *content; /* Données binaires à lire */ - phys_t init_diff; /* Position initiale de lecture*/ phys_t alen; /* Taille de l'aire utilisée */ - + vmpa2t pos; /* Tête de lecture */ bool forced_once; /* Préfigure une sortie rapide */ - phys_t i; /* Boucle de parcours */ - - - vmpa2t pos; /* Boucle de parcours */ - vmpa2t prev; /* Boucle de parcours */ - + vmpa2t prev; /* Sauvegarde de la tête */ GArchInstruction *instr; /* Instruction décodée */ phys_t diff; /* Volume de données traité */ - - - mrange_t range; /* Couverture de l'instruction */ - - bool done; /* Enregistrement effectué ? */ - - GArchInstruction *extra; /* Instruction supplémentaire */ - - /* Récupération des informations de base */ format = area->format; @@ -841,7 +750,7 @@ void load_code_from_mem_area(mem_area *area, mem_area *list, size_t count, GProc /* Progression dans les traitements */ - done = mark_range_in_mem_area_as_processed(area, instr, false, false); + done = insert_instr_into_mem_areas(list, count, instr, (mem_area *[]) { area }); if (!done) { @@ -868,7 +777,7 @@ void load_code_from_mem_area(mem_area *area, mem_area *list, size_t count, GProc extra != NULL; extra = g_preload_info_pop_instruction(G_PRELOAD_INFO(ctx))) { - insert_extra_instr_into_mem_areas(list, count, extra); + insert_instr_into_mem_areas_forced(list, count, extra); } /* Rupture du flot d'exécution ? */ @@ -943,7 +852,7 @@ static void load_data_from_mem_area(mem_area *area, GProcContext *ctx, const vmp /* Progression dans les traitements */ - done = mark_range_in_mem_area_as_processed(area, instr, false, false); + done = insert_instr_into_mem_areas(area, 1, instr, (mem_area *[]) { area }); if (!done) { @@ -1087,6 +996,12 @@ static GArchInstruction **get_instructions_from_mem_area(const mem_area *area, G } + +/* ---------------------------------------------------------------------------------- */ +/* TRAITEMENT DE ZONES PAR LOTS */ +/* ---------------------------------------------------------------------------------- */ + + /****************************************************************************** * * * Paramètres : list = listes de zones utable à consulter. * @@ -1128,11 +1043,151 @@ mem_area *find_memory_area_by_addr(mem_area *list, size_t count, const vmpa2t *a /****************************************************************************** * * +* Paramètres : areas = liste de zones délimitant des contenus à traiter. * +* count = nombre de zones à disposition. * +* instr = nouvelle instruction à venir insérer dans les zones.* +* advice = éventuelle indication pour la zone de départ. [OUT] * +* * +* Description : Insère une instruction dans un ensemble d'aires. * +* * +* Retour : true si l'enregistrement a bien été réalisé, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool insert_instr_into_mem_areas(mem_area *areas, size_t count, GArchInstruction *instr, mem_area **advice) +{ + bool result; /* Bilan d'action à renvoyer */ + const mrange_t *range; /* Emplacement d'instruction */ + const vmpa2t *start_addr; /* Localisation précise */ + mem_area *first_area; /* Zone d'appartenance */ + vmpa2t end_addr; /* Position finale nominale */ + mem_area *last_area; /* Zone d'arrivée */ + size_t first_index; /* Indice de la première zone */ + size_t last_index; /* Indice de la dernière zone */ + size_t i; /* Boucle de parcours */ + phys_t mark_start; /* Début du marquage */ + phys_t mark_len; /* Taille dudit marquage */ + + range = g_arch_instruction_get_range(instr); + start_addr = get_mrange_addr(range); + + /* Zone de départ */ + + first_area = NULL; + + if (advice != NULL && *advice != NULL) + { + if (mrange_contains_addr(&(*advice)->range, start_addr)) + first_area = *advice; + } + + if (first_area == NULL) + first_area = find_memory_area_by_addr(areas, count, start_addr); + + assert(first_area != NULL); + + /* Zone d'arrivée */ + + compute_mrange_end_addr(range, &end_addr); + + deminish_vmpa(&end_addr, 1); + + if (mrange_contains_addr(&first_area->range, &end_addr)) + last_area = first_area; + + else + { + last_area = find_memory_area_by_addr(areas, count, &end_addr); + + assert(last_area != NULL); + + } + + /* Verrouillage global ou local */ + + first_index = first_area - areas; + last_index = last_area - areas; + + if (first_index != last_index) + g_mutex_lock(first_area->global); + + for (i = first_index; i <= last_index; i++) + g_mutex_lock(&areas[i].mutex); + + if (first_index != last_index) + g_mutex_unlock(first_area->global); + + /* Vérification des disponibilités */ + + result = true; + + for (i = first_index; i <= last_index && result; i++) + { + if (i == first_index) + mark_start = compute_vmpa_diff(get_mrange_addr(&first_area->range), start_addr); + else + mark_start = 0; + + if (i == last_index) + mark_len = compute_vmpa_diff(get_mrange_addr(&last_area->range), &end_addr) + 1; + else + mark_len = get_mrange_length(&areas[i].range);; + + mark_len -= mark_start; + + result = _is_range_empty_in_mem_area(&areas[i], mark_start, mark_len); + + } + + if (!result) + goto no_space_available; + + /* Inscriptions */ + + for (i = first_index; i <= last_index; i++) + { + if (i == first_index) + mark_start = compute_vmpa_diff(get_mrange_addr(&first_area->range), start_addr); + else + mark_start = 0; + + if (i == last_index) + mark_len = compute_vmpa_diff(get_mrange_addr(&last_area->range), &end_addr) + 1; + else + mark_len = get_mrange_length(&areas[i].range);; + + mark_len -= mark_start; + + mark_range_in_mem_area_as_processed(&areas[i], + i == first_index ? instr : NULL, + mark_start, mark_len); + + } + + no_space_available: + + /* Déverrouillage global ou local */ + + for (i = first_index; i <= last_index; i++) + g_mutex_unlock(&areas[i].mutex); + + if (advice != NULL) + *advice = last_area; + + return result; + +} + + +/****************************************************************************** +* * * Paramètres : areas = liste de zones délimitant des contenus à traiter. * * count = nombre de zones à disposition. * * instr = nouvelle instruction à venir insérer dans les zones. * * * -* Description : Insère une instruction dans un découpage en aires. * +* Description : Force l'insertion d'une instruction dans un ensemble d'aires.* * * * Retour : - * * * @@ -1140,73 +1195,288 @@ mem_area *find_memory_area_by_addr(mem_area *list, size_t count, const vmpa2t *a * * ******************************************************************************/ -static void insert_extra_instr_into_mem_areas(mem_area *areas, size_t count, GArchInstruction *instr) +static void insert_instr_into_mem_areas_forced(mem_area *areas, size_t count, GArchInstruction *instr) { const mrange_t *range; /* Emplacement d'instruction */ - const vmpa2t *addr; /* Départ de cet emplacement */ - mem_area *area; /* Zone d'accueil désignée */ - VMPA_BUFFER(loc); /* Description d'un emplacement*/ - phys_t start; /* Point de départ */ -#ifndef NDEBUG - bool status; /* Validation d'une insertion */ -#endif + const vmpa2t *start_addr; /* Localisation précise */ + mem_area *first_area; /* Zone d'appartenance */ + vmpa2t end_addr; /* Position finale nominale */ + mem_area *last_area; /* Zone d'arrivée */ + size_t first_index; /* Indice de la première zone */ + size_t last_index; /* Indice de la dernière zone */ + size_t i; /* Boucle de parcours */ + bool available; /* Zone intégralement dispo ? */ + phys_t mark_start; /* Début du marquage */ + phys_t mark_len; /* Taille dudit marquage */ + mem_area *first_covered_area; /* Zone d'appartenance */ + size_t first_covered_index; /* Indice de la première zone */ + phys_t coverage_start; /* Début de zone à vider */ + phys_t coverage_iter; /* Parcours des zones à vider */ + phys_t coverage_len; /* Taille de cette même zone */ + mem_area *last_covered_area; /* Zone d'appartenance */ + size_t last_covered_index; /* Indice de la première zone */ + phys_t remaining; /* Couverture minimale restante*/ + phys_t length; /* Taille de zone restante */ range = g_arch_instruction_get_range(instr); - addr = get_mrange_addr(range); + start_addr = get_mrange_addr(range); + + /* Récupération des zones couvertes par l'instruction */ + + first_area = find_memory_area_by_addr(areas, count, start_addr); - /* Une aire d'accueil existe-t-elle ? */ + assert(first_area != NULL); - area = find_memory_area_by_addr(areas, count, addr); + compute_mrange_end_addr(range, &end_addr); - if (area == NULL) + deminish_vmpa(&end_addr, 1); + + if (mrange_contains_addr(&first_area->range, &end_addr)) + last_area = first_area; + + else { - vmpa2_virt_to_string(addr, MDS_UNDEFINED, loc, NULL); + last_area = find_memory_area_by_addr(areas, count, &end_addr); - log_variadic_message(LMT_WARNING, _("No place found for an instruction located at %s."), loc); - goto ieiima_failed; + assert(last_area != NULL); } - /** - * Une instruction ne peut avoir une adresse virtuelle que s'il - * est compris dans une zone chargée en mémoire (en toute logique). - */ - assert(has_virt_addr(get_mrange_addr(&area->range)) == has_virt_addr(addr)); + /* Verrouillage global */ + + first_index = first_area - areas; + last_index = last_area - areas; - /* L'instruction est-elle accueillie dans son intégralité ? */ + g_mutex_lock(first_area->global); - start = compute_vmpa_diff(get_mrange_addr(&area->range), addr); + for (i = first_index; i <= last_index; i++) + g_mutex_lock(&areas[i].mutex); - if (start + get_mrange_length(range) > get_mrange_length(&area->range)) + /* Validation des disponibilités */ + + available = true; + + for (i = first_index; i <= last_index && available; i++) { - vmpa2_virt_to_string(addr, MDS_UNDEFINED, loc, NULL); + if (i == first_index) + mark_start = compute_vmpa_diff(get_mrange_addr(&first_area->range), start_addr); + else + mark_start = 0; + + if (i == last_index) + mark_len = compute_vmpa_diff(get_mrange_addr(&last_area->range), &end_addr) + 1; + else + mark_len = get_mrange_length(&areas[i].range);; - log_variadic_message(LMT_WARNING, _("The instruction located at %s is too big for one place only."), loc); - goto ieiima_failed; + mark_len -= mark_start; + + available = _is_range_empty_in_mem_area(&areas[i], mark_start, mark_len); } - /* Inscription d'une instruction (sans retour arrière possible :/ ) */ + /* Si la couverture nécessite une mise à disposition */ + + if (!available) + { + /** + * Un cas de remplacement forcé intervient en ARM, lorsque qu'une + * instruction utilise une valeur immédiate placée dans le code. + * + * Cette valeur doit être référencée en tant que donnée. + * + * Mais cette même valeur a pu être désassemblée en tant que code + * exécutable si le flot d'exécution s'est poursuivi jusqu'à elle. + * + * C'est par exemple le cas lors de l'utilisation d'appels système + * en assembleur, qui ne sont pas reconnus en tant qu'instructions + * cassant le flot d'exécution (typiquement : un exit()). + * + * On réinitialise donc la zone couverte par la nouvelle instruction. + */ + + first_covered_area = first_area; + first_covered_index = first_index; + + coverage_start = compute_vmpa_diff(get_mrange_addr(&first_covered_area->range), start_addr); + + coverage_iter = coverage_start; + + coverage_len = 0; + + /** + * Par ailleurs, il se peut que la nouvelle instruction ne couvre + * que partiellement une instruction existante. + * + * Il faut donc dans ce cas remonter la table des enregistrements + * pour retrouver l'instruction à l'origine de la couverture à remplacer. + */ + + while (first_covered_area->instructions[coverage_start] == NULL) + { + if (coverage_start == 0) + { + assert(first_covered_index > 0); + + first_covered_area = &areas[--first_covered_index]; + g_mutex_lock(&first_covered_area->mutex); + + assert(get_mrange_length(&first_covered_area->range) > 0); + + coverage_start = get_mrange_length(&first_covered_area->range) - 1; + + } + + else + coverage_start--; + + coverage_len++; + + } + + /** + * De la même manière, on étend la couverture au besoin dans l'autre sens. + */ + + last_covered_area = last_area; + last_covered_index = last_index; + + remaining = get_mrange_length(range); + + while (remaining > 0) + { + length = get_mrange_length(&last_covered_area->range) - coverage_iter; + + if (remaining >= length) + { + coverage_len += length; + remaining -= length; + + if (remaining > 0) + { + assert((last_covered_index + 1) < count); + + last_covered_area = &areas[++last_covered_index]; + g_mutex_lock(&last_covered_area->mutex); + + coverage_iter = 0; + + } + + } + + else + { + coverage_len += remaining; + remaining = 0; + } + + } + + g_mutex_unlock(first_area->global); + + assert(coverage_len >= get_mrange_length(range)); + + /* Réinitialisation */ + + for (i = first_covered_index; i <= last_covered_index; i++) + { + if (i == first_covered_index) + mark_start = coverage_start; + else + mark_start = 0; + + if (i == last_covered_index) + mark_len = coverage_len; + else + { + mark_len = get_mrange_length(&areas[i].range);; + + assert(mark_len > mark_start); + + mark_len -= mark_start; + + } + + coverage_len -= mark_len; + + unmark_range_in_mem_area_as_processed(&areas[i], mark_start, mark_len); + + } + + /* Libération des zones frontalières */ + + for (i = first_covered_index; i < first_index; i++) + g_mutex_unlock(&areas[i].mutex); + + for (i = (last_index + 1); i <= last_covered_index; i++) + g_mutex_unlock(&areas[i].mutex); + + /* Vérification ultime */ #ifndef NDEBUG - status = mark_range_in_mem_area_as_processed(area, instr, true, false); - assert(status); -#else - mark_range_in_mem_area_as_processed(area, instr, true, false); + + available = true; + + for (i = first_index; i <= last_index && available; i++) + { + if (i == first_index) + mark_start = compute_vmpa_diff(get_mrange_addr(&first_area->range), start_addr); + else + mark_start = 0; + + if (i == last_index) + mark_len = compute_vmpa_diff(get_mrange_addr(&last_area->range), &end_addr) + 1; + else + mark_len = get_mrange_length(&areas[i].range);; + + mark_len -= mark_start; + + available = _is_range_empty_in_mem_area(&areas[i], mark_start, mark_len); + + } + + assert(available); + #endif - return; + } - ieiima_failed: + else + g_mutex_unlock(first_area->global); - g_object_unref(G_OBJECT(instr)); + /* Inscription */ + + for (i = first_index; i <= last_index; i++) + { + if (i == first_index) + mark_start = compute_vmpa_diff(get_mrange_addr(&first_area->range), start_addr); + else + mark_start = 0; + + if (i == last_index) + mark_len = compute_vmpa_diff(get_mrange_addr(&last_area->range), &end_addr) + 1; + else + mark_len = get_mrange_length(&areas[i].range);; + + mark_len -= mark_start; + + mark_range_in_mem_area_as_processed(&areas[i], + i == first_index ? instr : NULL, + mark_start, mark_len); + + } + + /* Déverrouillage des zones traitées restantes */ + + for (i = first_index; i <= last_index; i++) + g_mutex_unlock(&areas[i].mutex); } /****************************************************************************** * * -* Paramètres : list = liste de zones délimitant des contenus à traiter. * +* Paramètres : areas = liste de zones délimitant des contenus à traiter. * * count = nombre de zones à disposition. * * binary = représentation de binaire chargé. * * ctx = contexte offert en soutien à un désassemblage. * @@ -1221,12 +1491,12 @@ static void insert_extra_instr_into_mem_areas(mem_area *areas, size_t count, GAr * * ******************************************************************************/ -void ensure_all_mem_areas_are_filled(mem_area *list, size_t count, GProcContext *ctx, GtkStatusStack *status, activity_id_t id) +void ensure_all_mem_areas_are_filled(mem_area *areas, size_t count, GProcContext *ctx, GtkStatusStack *status, activity_id_t id) { size_t i; /* Boucle de parcours */ for (i = 0; i < count; i++) - fill_mem_area(&list[i], list, count, ctx, status, id); + fill_mem_area(&areas[i], areas, count, ctx, status, id); } @@ -1375,6 +1645,7 @@ static void g_area_collector_process(GAreaCollector *collector, GtkStatusStack * * * * Paramètres : id = identifiant pour signaler la progression courante. * * binary = binaire chargé à conserver dans les zones définies.* +* global = verrou pour les accès sur plusieurs zones. * * info = préchargements effectués via le format binaire. * * first = localisation du début de la portion à traiter. * * last = localisation de la fin de la portion à traiter. * @@ -1388,7 +1659,7 @@ static void g_area_collector_process(GAreaCollector *collector, GtkStatusStack * * * ******************************************************************************/ -static GAreaCollector *g_area_collector_new_intro(activity_id_t id, GLoadedBinary *binary, phys_t first, phys_t last, bool closing) +static GAreaCollector *g_area_collector_new_intro(activity_id_t id, GLoadedBinary *binary, GMutex *global, phys_t first, phys_t last, bool closing) { GAreaCollector *result; /* Tâche à retourner */ @@ -1404,6 +1675,8 @@ static GAreaCollector *g_area_collector_new_intro(activity_id_t id, GLoadedBinar result->binary = binary; g_object_ref(G_OBJECT(binary)); + result->global = global; + result->first = first; result->last = last; @@ -1461,7 +1734,7 @@ static void g_area_collector_do_compute(GAreaCollector *collector, GtkStatusStac area = &(*list)[*count - 1]; - init_mem_area_from_addr(area, old, diff, collector->binary); + init_mem_area_from_addr(area, old, diff, collector->binary, collector->global); area->is_exec = exec; /* Avancée du curseur */ @@ -1616,11 +1889,20 @@ mem_area *collect_memory_areas(wgroup_id_t gid, GtkStatusStack *status, GLoadedB phys_t run_size; /* Volume réparti par exécution*/ GWorkQueue *queue; /* Gestionnaire de différés */ activity_id_t id; /* Identifiant de progression */ + GMutex *global; /* Atomicité sur zones multi. */ guint i; /* Boucle de parcours */ phys_t first; /* Début de zone de traitement */ bool closing; /* Détection de fin en amont */ phys_t last; /* Fin de zone de traitement */ + /* Création d'un verrou global */ + + global = (GMutex *)malloc(sizeof(GMutex)); + + g_mutex_init(global); + + /* Lancement des traitements */ + runs_count = g_get_num_processors(); collectors = (GAreaCollector **)calloc(runs_count, sizeof(GAreaCollector *)); @@ -1642,7 +1924,7 @@ mem_area *collect_memory_areas(wgroup_id_t gid, GtkStatusStack *status, GLoadedB else last = first + run_size; - collectors[i] = g_area_collector_new_intro(id, binary, first, last, closing); + collectors[i] = g_area_collector_new_intro(id, binary, global, first, last, closing); g_object_ref(G_OBJECT(collectors[i])); g_work_queue_schedule_work(queue, G_DELAYED_WORK(collectors[i]), gid); @@ -1734,84 +2016,21 @@ static GAreaCollector *g_area_collector_new_insert(activity_id_t id, mem_area *a static void g_area_collector_do_insert(GAreaCollector *collector, GtkStatusStack *status) { - mem_area *area; /* Zone d'appartenance */ - size_t i; /* Boucle de parcours */ + mem_area *last; /* Zone d'appartenance */ + size_t i; /* Boucle de parcours #1 */ GArchInstruction *instr; /* Instruction à analyser */ - const mrange_t *range; /* Emplacement d'instruction */ - const vmpa2t *addr; /* Localisation précise */ -#ifndef NDEBUG - bool inserted; /* Validation d'une insertion */ -#endif - vmpa2t iend; /* Position finale nominale */ - vmpa2t pos; /* Tête de lecture */ - phys_t remaining; /* Zone supplémentaire couverte*/ - phys_t available; /* Zone disponible */ - phys_t mark_len; /* Zone à marquer comme traitée*/ + bool done; /* Insertion réalisée ? */ - area = NULL; + last = NULL; for (i = collector->start; i < collector->stop; i++) { instr = _g_preload_info_grab_instruction(collector->info, i); - range = g_arch_instruction_get_range(instr); - addr = get_mrange_addr(range); - - if (area != NULL) - { - if (!mrange_contains_addr(&area->range, addr)) - area = NULL; - } - - if (area == NULL) - area = find_memory_area_by_addr(collector->areas, collector->available, addr); - - assert(area != NULL); - -#ifndef NDEBUG - inserted = mark_range_in_mem_area_as_processed(area, instr, false, true); - assert(inserted); -#else - mark_range_in_mem_area_as_processed(area, instr, false, true); -#endif - - /** - * Si l'instruction n'a pas pu être accueillie dans son intégralité, on - * ne rassemble pas les zones pour autant. - * - * On pourrait imaginer sauver la situation pour les segments débordant - * sur une section, mais le problème resterait posé pour des zones - * définies par l'utilisateur au sein d'une section. - * - * On note donc la place occupée par le débordement éventuel ici. - */ - - compute_mrange_end_addr(g_arch_instruction_get_range(instr), &iend); - compute_mrange_end_addr(&area->range, &pos); - - if (cmp_vmpa(&iend, &pos) > 0) - { - remaining = compute_vmpa_diff(&pos, &iend); - - while (remaining > 0) - { - area = find_memory_area_by_addr(collector->areas, collector->available, &pos); - assert(area != NULL); - - available = get_mrange_length(&area->range); - if (remaining > available) - mark_len = available; - else - mark_len = remaining; + done = insert_instr_into_mem_areas(collector->areas, collector->available, instr, &last); - mark_range_in_mem_area_as_overlapping(area, mark_len); - - advance_vmpa(&pos, mark_len); - remaining -= mark_len; - - } - - } + if (!done) + g_object_unref(G_OBJECT(instr)); gtk_status_stack_update_activity_value(status, collector->id, 1); @@ -1981,6 +2200,7 @@ static void g_area_collector_do_collect(GAreaCollector *collector, GtkStatusStac GArchInstruction **collect_disassembled_instructions(wgroup_id_t gid, GtkStatusStack *status, mem_area *list, size_t acount, size_t *icount) { GArchInstruction **result; /* Liste finale à retourner */ + GMutex *global; /* Atomicité sur zones multi. */ guint runs_count; /* Qté d'exécutions parallèles */ GAreaCollector **collectors; /* Collecteurs à suivre */ size_t run_size; /* Volume réparti par exécution*/ @@ -1990,6 +2210,18 @@ GArchInstruction **collect_disassembled_instructions(wgroup_id_t gid, GtkStatusS size_t begin; /* Début de bloc de traitement */ size_t end; /* Fin d'un bloc de traitement */ + /* Suppression du verrou global */ + + assert(acount > 0); + + global = list[0].global; + + g_mutex_clear(global); + + free(global); + + /* Lancement des traitements */ + runs_count = g_get_num_processors(); collectors = (GAreaCollector **)calloc(runs_count, sizeof(GAreaCollector *)); diff --git a/src/analysis/disass/area.h b/src/analysis/disass/area.h index 818b8a2..d1e59d9 100644 --- a/src/analysis/disass/area.h +++ b/src/analysis/disass/area.h @@ -44,6 +44,11 @@ typedef struct _mem_area mem_area; /* Procède au désassemblage d'un contenu binaire exécutable. */ void load_code_from_mem_area(mem_area *, mem_area *, size_t, GProcContext *, const vmpa2t *, bool, GtkStatusStack *, activity_id_t); + + +/* -------------------------- TRAITEMENT DE ZONES PAR LOTS -------------------------- */ + + /* Détermine une liste de zones contigües à traiter. */ mem_area *find_memory_area_by_addr(mem_area *, size_t, const vmpa2t *); @@ -51,6 +56,7 @@ mem_area *find_memory_area_by_addr(mem_area *, size_t, const vmpa2t *); void ensure_all_mem_areas_are_filled(mem_area *, size_t, GProcContext *, GtkStatusStack *, activity_id_t); + /* ----------------------- MANIPULATIONS PARALLELES DES ZONES ----------------------- */ |