From 27d2c90a6a609dcef47880fa43241b77e66f5d85 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Wed, 23 May 2018 21:20:52 +0200
Subject: Implemented a stronger coverage of disassembled areas.

---
 src/analysis/disass/area.c | 844 +++++++++++++++++++++++++++++----------------
 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 ----------------------- */
 
 
-- 
cgit v0.11.2-87-g4458