summaryrefslogtreecommitdiff
path: root/src/analysis/disass/area.c
diff options
context:
space:
mode:
authorCyrille Bagard <nocbos@gmail.com>2018-05-23 19:20:52 (GMT)
committerCyrille Bagard <nocbos@gmail.com>2018-05-23 19:20:52 (GMT)
commit27d2c90a6a609dcef47880fa43241b77e66f5d85 (patch)
tree0bfdec6d2d95cdffd1500f684739329f509593c7 /src/analysis/disass/area.c
parent3fd2fb5411f532120b3e25942aaafc7150ab078b (diff)
Implemented a stronger coverage of disassembled areas.
Diffstat (limited to 'src/analysis/disass/area.c')
-rw-r--r--src/analysis/disass/area.c844
1 files changed, 538 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 *));