summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCyrille Bagard <nocbos@gmail.com>2015-12-19 20:22:14 (GMT)
committerCyrille Bagard <nocbos@gmail.com>2015-12-19 20:22:14 (GMT)
commit5710f9d5be56b427ccfa48f6a730d70396817efe (patch)
tree4b694774ce446295ef5b01a7df28bd8160b97025
parent8ff010a34762737016624a68f593d0e6736d4349 (diff)
Fixed several bugs when processing concurrent delayed works.
-rw-r--r--ChangeLog17
-rw-r--r--src/analysis/disass/fetch.c88
-rw-r--r--src/arch/context-int.h2
-rw-r--r--src/arch/context.c55
-rw-r--r--src/arch/context.h6
-rw-r--r--src/glibext/delayed.c203
-rw-r--r--src/glibext/delayed.h11
7 files changed, 318 insertions, 64 deletions
diff --git a/ChangeLog b/ChangeLog
index 31dca7f..a6c2c2c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+15-12-19 Cyrille Bagard <nocbos@gmail.com>
+
+ * src/analysis/disass/fetch.c:
+ Track the remaining drop points for the delayed work group.
+
+ * src/arch/context-int.h:
+ * src/arch/context.c:
+ * src/arch/context.h:
+ Use an external counter to track the added drop points.
+
+ * src/glibext/delayed.c:
+ * src/glibext/delayed.h:
+ Fix several bugs when processing concurrent delayed works. Count precisely
+ the remaining works to wait for. Avoid to generate group identifiers equal
+ to DEFAULT_WORK_GROUP. Fix a bug by handling the "no status bar" case. Fix
+ an Out-Of-Bound access when creating new groups.
+
15-12-17 Cyrille Bagard <nocbos@gmail.com>
* src/arch/arm/context.c:
diff --git a/src/analysis/disass/fetch.c b/src/analysis/disass/fetch.c
index 5caf573..29d8923 100644
--- a/src/analysis/disass/fetch.c
+++ b/src/analysis/disass/fetch.c
@@ -309,7 +309,8 @@ static void g_delayed_fetching_process(GDelayedFetching *fetching, GtkExtStatusB
/* Poursuit l'analyse à partir des points d'entrée découverts. */
static void follow_execution_flow_v2(GProcContext *, const GDelayedFetching *);
-
+/* Etudie le besoin d'attendre d'avantage de prochaines tâches. */
+static bool check_if_extra_wait_is_needed(GWorkQueue *, wgroup_id_t, GProcContext *);
@@ -331,11 +332,14 @@ static void follow_execution_flow_v2(GProcContext *, const GDelayedFetching *);
static void follow_execution_flow_v2(GProcContext *ctx, const GDelayedFetching *template)
{
GWorkQueue *queue; /* Gestionnaire de différés */
+ gint *remaining_counter; /* Compteur à considérer */
virt_t virt; /* Adresse de départ dépilée */
GDelayedFetching *fetching; /* Récupération à mener */
queue = get_work_queue();
+ remaining_counter = (gint *)g_object_get_data(G_OBJECT(ctx), "remaining_counter");
+
while (g_proc_context_pop_drop_point(ctx, &virt))
{
fetching = g_delayed_fetching_new(template, virt);
@@ -351,6 +355,16 @@ static void follow_execution_flow_v2(GProcContext *ctx, const GDelayedFetching *
g_work_queue_schedule_work(queue, G_DELAYED_WORK(fetching), template->gid);
+ /**
+ * Le décompte n'est réalisé qu'après la programmation de la tâche.
+ * Ainsi, lors de l'attente de la fin des traitements, on a la garantie
+ * de ne pas avoir de trou entre le dépilement des points et la programmation
+ * des tâches de traitement associées.
+ */
+
+ if (g_atomic_int_dec_and_test(remaining_counter))
+ g_work_queue_wake_up_waiters(queue, template->gid);
+
}
}
@@ -358,6 +372,45 @@ static void follow_execution_flow_v2(GProcContext *ctx, const GDelayedFetching *
/******************************************************************************
* *
+* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. *
+* id = identifiant d'un groupe de travail. *
+* ctx = contexte de désass. avec une nouvelle entrée. *
+* *
+* Description : Etudie le besoin d'attendre d'avantage de prochaines tâches. *
+* *
+* Retour : true pour attendre d'avantage, false sinon. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool check_if_extra_wait_is_needed(GWorkQueue *queue, wgroup_id_t id, GProcContext *ctx)
+{
+ bool result; /* Bilan à retourner */
+ gint *remaining_counter; /* Compteur à considérer */
+
+ remaining_counter = (gint *)g_object_get_data(G_OBJECT(ctx), "remaining_counter");
+
+ result = (g_atomic_int_get(remaining_counter) > 0);
+
+ return result;
+
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+* *
* Paramètres : binary = représentation de binaire chargé. *
* ctx = contexte offert en soutien à un désassemblage. *
* areas = liste de zones contenant des données à traiter. *
@@ -435,15 +488,9 @@ GArchInstruction *disassemble_binary_content(const GLoadedBinary *binary, wgroup
GBinContent *content; /* Contenu binaire à manipuler */
phys_t length; /* Taille des données à lire */
GWorkQueue *queue; /* Gestionnaire de différés */
+ gint remaining_counter; /* Quantité de points restants */
double done; /* Portion de travail accompli */
-
-
-
-
-
-
-
/* Constitution du modèle de référence */
template.gid = gid;
@@ -463,24 +510,33 @@ GArchInstruction *disassemble_binary_content(const GLoadedBinary *binary, wgroup
/* Amorce des traitements */
- g_signal_connect(template.ctx, "drop-point-pushed", G_CALLBACK(follow_execution_flow_v2), &template);
-
queue = get_work_queue();
+ g_atomic_int_set(&remaining_counter, 0);
+
+ g_object_set_data(G_OBJECT(template.ctx), "remaining_counter", &remaining_counter);
+
+ g_proc_context_attach_counter(template.ctx, &remaining_counter);
+
/**
* Première phase de désassemblage : suivi des chemins tracés.
*/
+
+ g_work_queue_set_extra_wait_callback(queue, gid,
+ (wait_for_incoming_works_cb)check_if_extra_wait_is_needed,
+ template.ctx);
+
+ g_signal_connect(template.ctx, "drop-point-pushed", G_CALLBACK(follow_execution_flow_v2), &template);
+
template.info = init_progessive_status(statusbar,
_("Disassembling following the execution flow..."),
0, length);
-
+
g_binary_format_setup_disassembling_context(format, template.ctx);
g_work_queue_wait_for_completion(queue, gid);
- printf("===================== DONE !\n");
-
done = get_current_progessive_status(template.info);
fini_progessive_status(template.info);
@@ -495,6 +551,12 @@ GArchInstruction *disassemble_binary_content(const GLoadedBinary *binary, wgroup
ensure_all_mem_areas_are_filled(template.areas, template.count, template.ctx, template.info);
+ g_work_queue_wait_for_completion(queue, gid);
+
+ g_work_queue_set_extra_wait_callback(queue, gid, NULL, NULL);
+
+ g_object_set_data(G_OBJECT(template.ctx), "remaining_counter", NULL);
+
fini_progessive_status(template.info);
/**
diff --git a/src/arch/context-int.h b/src/arch/context-int.h
index 885c2f6..59a06bb 100644
--- a/src/arch/context-int.h
+++ b/src/arch/context-int.h
@@ -54,6 +54,8 @@ struct _GProcContext
size_t esyms_count; /* Nombres de nouveautés */
GMutex es_access; /* Accès à cette même liste */
+ gint *counter;
+
};
diff --git a/src/arch/context.c b/src/arch/context.c
index bb1d80a..98eebb2 100644
--- a/src/arch/context.c
+++ b/src/arch/context.c
@@ -104,6 +104,26 @@ static void g_proc_context_init(GProcContext *ctx)
/******************************************************************************
* *
+* Paramètres : ctx = contexte de désassemblage à mettre à jour. *
+* counter = adresse du compteur à modifier. *
+* *
+* Description : Enregistre un compteur pour le décompte des points à traiter.*
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_proc_context_attach_counter(GProcContext *ctx, gint *counter)
+{
+ ctx->counter = counter;
+
+}
+
+
+/******************************************************************************
+* *
* Paramètres : ctx = contexte de désassemblage à compléter. *
* level = indication de priorité et d'origine de l'adresse. *
* addr = adresse d'un nouveau point de départ à traiter. *
@@ -155,6 +175,9 @@ void g_proc_context_push_drop_point(GProcContext *ctx, unsigned int level, virt_
g_mutex_lock(&ctx->dp_access);
+ if (ctx->counter != NULL)
+ g_atomic_int_inc(ctx->counter);
+
G_PROC_CONTEXT_GET_CLASS(ctx)->push_point(ctx, level, addr, ap);
g_mutex_unlock(&ctx->dp_access);
@@ -168,38 +191,6 @@ void g_proc_context_push_drop_point(GProcContext *ctx, unsigned int level, virt_
/******************************************************************************
* *
-* Paramètres : ctx = contexte de désassemblage à consulter. *
-* addr = adresse de mémoire virtuelle à rechercher. *
-* *
-* Description : Précise si une adresse donnée figure comme point de départ. *
-* *
-* Retour : true si l'adresse est connue en interne, false sinon. *
-* *
-* Remarques : - *
-* *
-******************************************************************************/
-
-bool g_proc_context_has_addr_as_drop_points(GProcContext *ctx, virt_t addr)
-{
- bool result; /* Bilan à retourner */
- size_t i; /* Boucle de parcours */
-
- result = false;
-
- g_mutex_lock(&ctx->dp_access);
-
- for (i = 0; i < ctx->dp_count && !result; i++)
- result = (ctx->drop_points[i] == addr);
-
- g_mutex_unlock(&ctx->dp_access);
-
- return result;
-
-}
-
-
-/******************************************************************************
-* *
* Paramètres : ctx = contexte de désassemblage à compléter. *
* virt = adresse d'un point de départ de code à traiter. *
* *
diff --git a/src/arch/context.h b/src/arch/context.h
index 5dffd6a..334eaf1 100644
--- a/src/arch/context.h
+++ b/src/arch/context.h
@@ -51,12 +51,12 @@ typedef struct _GProcContextClass GProcContextClass;
/* Indique le type définit par la GLib pour le contexte de processeur. */
GType g_proc_context_get_type(void);
+/* Enregistre un compteur pour le décompte des points à traiter. */
+void g_proc_context_attach_counter(GProcContext *, gint *);
+
/* Ajoute une adresse virtuelle comme point de départ de code. */
void g_proc_context_push_drop_point(GProcContext *, unsigned int, virt_t, ...);
-/* Précise si une adresse donnée figure comme point de départ. */
-bool g_proc_context_has_addr_as_drop_points(GProcContext *, virt_t);
-
/* Fournit une adresse virtuelle comme point de départ de code. */
bool g_proc_context_pop_drop_point(GProcContext *, virt_t *);
diff --git a/src/glibext/delayed.c b/src/glibext/delayed.c
index 967865b..a83fae4 100644
--- a/src/glibext/delayed.c
+++ b/src/glibext/delayed.c
@@ -78,11 +78,15 @@ typedef struct _GWorkGroup
GMutex mutex; /* Verrou pour l'accès */
GCond cond; /* Réveil pour un traitement */
GCond wait_cond; /* Réveil d'attente de fin */
+ gint pending; /* Tâches en cours d'exécution */
GThread **threads; /* Procédure de traitement */
guint threads_count; /* Nombre de procédures */
bool force_exit; /* Procédure d'arrêt */
+ wait_for_incoming_works_cb callback; /* Encadre les attentes de fin */
+ void *data; /* Données à associer */
+
} GWorkGroup;
/* File de traitement pour un type donné (classe) */
@@ -121,7 +125,13 @@ static void g_work_group_schedule(GWorkGroup *, GDelayedWork *);
static void *g_work_group_process(GWorkGroup *);
/* Attend que toutes les tâches d'un groupe soient traitées. */
-static void g_work_group_wait_for_completion(GWorkGroup *);
+static void g_work_group_wait_for_completion(GWorkGroup *, GWorkQueue *);
+
+/* Modifie les conditions d'attente des fins d'exécutions. */
+static void g_work_group_set_extra_wait_callback(GWorkGroup *, wait_for_incoming_works_cb, void *);
+
+/* Force un réveil d'une attente en cours pour la confirmer. */
+static void g_work_group_wake_up_waiters(GWorkGroup *);
@@ -166,6 +176,9 @@ static void g_work_queue_finalize(GWorkQueue *);
/* Donne l'assurance de l'existence d'un groupe de travail. */
static GWorkGroup *g_work_queue_ensure_group_exists(GWorkQueue *, wgroup_id_t);
+/* Fournit le groupe de travail correspondant à un identifiant. */
+static GWorkGroup *g_work_queue_find_group_for_id(GWorkQueue *, wgroup_id_t);
+
/* ---------------------------------------------------------------------------------- */
@@ -379,6 +392,8 @@ static void g_work_group_init(GWorkGroup *group)
g_cond_init(&group->cond);
g_cond_init(&group->wait_cond);
+ g_atomic_int_set(&group->pending, 0);
+
group->threads_count = g_get_num_processors();
group->threads = (GThread **)calloc(group->threads_count, sizeof(GThread *));
@@ -401,6 +416,9 @@ static void g_work_group_init(GWorkGroup *group)
group->force_exit = false;
+ group->callback = NULL;
+ group->data = NULL;
+
}
@@ -491,8 +509,11 @@ static GWorkGroup *g_work_group_new(wgroup_id_t id, GtkExtStatusBar *statusbar)
result->id = id;
- result->statusbar = statusbar;
- g_object_ref(statusbar);
+ if (statusbar != NULL)
+ {
+ result->statusbar = statusbar;
+ g_object_ref(statusbar);
+ }
return result;
@@ -535,6 +556,8 @@ static void g_work_group_schedule(GWorkGroup *group, GDelayedWork *work)
{
g_mutex_lock(&group->mutex);
+ g_atomic_int_inc(&group->pending);
+
delayed_work_list_add_tail(work, &group->works);
g_cond_signal(&group->cond);
@@ -568,7 +591,10 @@ static void *g_work_group_process(GWorkGroup *group)
g_cond_wait(&group->cond, &group->mutex);
if (group->force_exit)
+ {
+ g_mutex_unlock(&group->mutex);
break;
+ }
work = group->works;
delayed_work_list_del(work, &group->works);
@@ -579,7 +605,8 @@ static void *g_work_group_process(GWorkGroup *group)
g_object_unref(G_OBJECT(work));
- g_cond_broadcast(&group->wait_cond);
+ if (g_atomic_int_dec_and_test(&group->pending))
+ g_cond_broadcast(&group->wait_cond);
}
@@ -591,6 +618,7 @@ static void *g_work_group_process(GWorkGroup *group)
/******************************************************************************
* *
* Paramètres : group = groupe dont les conclusions sont attendues. *
+* queue = queue d'appartenance pour les appels externes. *
* *
* Description : Attend que toutes les tâches d'un groupe soient traitées. *
* *
@@ -600,18 +628,78 @@ static void *g_work_group_process(GWorkGroup *group)
* *
******************************************************************************/
-static void g_work_group_wait_for_completion(GWorkGroup *group)
+static void g_work_group_wait_for_completion(GWorkGroup *group, GWorkQueue *queue)
{
+ wait_for_incoming_works_cb callback; /* Procédure complémentaire */
+
+ bool no_extra_check(GWorkQueue *_q, wgroup_id_t _id, void *_data)
+ {
+ return false;
+ }
+
+ callback = group->callback != NULL ? group->callback : no_extra_check;
+
g_mutex_lock(&group->mutex);
- while (dl_list_empty(group->works) && !group->force_exit)
+ /**
+ * On attend que :
+ * - la liste des tâches programmées soit vide.
+ * - il n'existe plus de tâche en cours.
+ * - rien n'indique que de nouvelles tâches supplémentaires vont arriver.
+ */
+
+ while ((g_atomic_int_get(&group->pending) > 0 || callback(queue, group->id, group->data))
+ && !group->force_exit)
+ {
g_cond_wait(&group->wait_cond, &group->mutex);
+ }
g_mutex_unlock(&group->mutex);
}
+/******************************************************************************
+* *
+* Paramètres : group = groupe dont les paramètres sont à modifier. *
+* callback = éventuelle fonction à appeler ou NULL. *
+* data = données devant accompagner l'appel. *
+* *
+* Description : Modifie les conditions d'attente des fins d'exécutions. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_work_group_set_extra_wait_callback(GWorkGroup *group, wait_for_incoming_works_cb callback, void *data)
+{
+ group->callback = callback;
+ group->data = data;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.*
+* id = identifiant d'un groupe de travail. *
+* *
+* Description : Force un réveil d'une attente en cours pour la confirmer. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_work_group_wake_up_waiters(GWorkGroup *group)
+{
+ g_cond_broadcast(&group->wait_cond);
+
+}
+
/* ---------------------------------------------------------------------------------- */
@@ -816,7 +904,7 @@ static GWorkGroup *g_work_queue_ensure_group_exists(GWorkQueue *queue, wgroup_id
queue->groups_count * sizeof(GWorkGroup *));
result = g_work_group_new(id, queue->statusbar);
- queue->groups[i] = result;
+ queue->groups[queue->groups_count - 1] = result;
}
@@ -843,7 +931,7 @@ wgroup_id_t g_work_queue_define_work_group(GWorkQueue *queue)
g_mutex_lock(&queue->mutex);
- result = queue->generator++;
+ result = ++queue->generator;
g_work_queue_ensure_group_exists(queue, result);
@@ -932,35 +1020,118 @@ void g_work_queue_schedule_work(GWorkQueue *queue, GDelayedWork *work, wgroup_id
* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. *
* id = identifiant d'un groupe de travail. *
* *
-* Description : Attend que toutes les tâches d'un groupe soient traitées. *
+* Description : Fournit le groupe de travail correspondant à un identifiant. *
* *
-* Retour : - *
+* Retour : Eventuel groupe existant trouvé ou NULL si aucun. *
* *
* Remarques : - *
* *
******************************************************************************/
-void g_work_queue_wait_for_completion(GWorkQueue *queue, wgroup_id_t id)
+static GWorkGroup *g_work_queue_find_group_for_id(GWorkQueue *queue, wgroup_id_t id)
{
+ GWorkGroup *result; /* Trouvaille à retourner */
size_t i; /* Boucle de parcours */
- GWorkGroup *group; /* Groupe de travail à attendre*/
- group = NULL;
+ result = NULL;
g_mutex_lock(&queue->mutex);
for (i = 0; i < queue->groups_count; i++)
if (g_work_group_get_id(queue->groups[i]) == id)
{
- group = queue->groups[i];
- g_object_ref(G_OBJECT(group));
+ result = queue->groups[i];
+ g_object_ref(G_OBJECT(result));
+ break;
}
g_mutex_unlock(&queue->mutex);
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. *
+* id = identifiant d'un groupe de travail. *
+* *
+* Description : Attend que toutes les tâches d'un groupe soient traitées. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_work_queue_wait_for_completion(GWorkQueue *queue, wgroup_id_t id)
+{
+ GWorkGroup *group; /* Groupe de travail à attendre*/
+
+ group = g_work_queue_find_group_for_id(queue, id);
+
+ if (group != NULL)
+ {
+ g_work_group_wait_for_completion(group, queue);
+ g_object_unref(G_OBJECT(group));
+ }
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.*
+* id = identifiant d'un groupe de travail. *
+* callback = éventuelle fonction à appeler ou NULL. *
+* data = données devant accompagner l'appel. *
+* *
+* Description : Modifie les conditions d'attente des fins d'exécutions. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_work_queue_set_extra_wait_callback(GWorkQueue *queue, wgroup_id_t id, wait_for_incoming_works_cb callback, void *data)
+{
+ GWorkGroup *group; /* Groupe de travail à traiter */
+
+ group = g_work_queue_find_group_for_id(queue, id);
+
+ if (group != NULL)
+ {
+ g_work_group_set_extra_wait_callback(group, callback, data);
+ g_object_unref(G_OBJECT(group));
+ }
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.*
+* id = identifiant d'un groupe de travail. *
+* *
+* Description : Force un réveil d'une attente en cours pour la confirmer. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_work_queue_wake_up_waiters(GWorkQueue *queue, wgroup_id_t id)
+{
+ GWorkGroup *group; /* Groupe de travail à traiter */
+
+ group = g_work_queue_find_group_for_id(queue, id);
+
if (group != NULL)
{
- g_work_group_wait_for_completion(group);
+ g_work_group_wake_up_waiters(group);
g_object_unref(G_OBJECT(group));
}
diff --git a/src/glibext/delayed.h b/src/glibext/delayed.h
index 0a27c93..4902e68 100644
--- a/src/glibext/delayed.h
+++ b/src/glibext/delayed.h
@@ -106,5 +106,16 @@ void g_work_queue_schedule_work(GWorkQueue *, GDelayedWork *, wgroup_id_t);
void g_work_queue_wait_for_completion(GWorkQueue *, wgroup_id_t);
+/* Etudie le besoin d'attendre d'avantage de prochaines tâches. */
+typedef bool (* wait_for_incoming_works_cb) (GWorkQueue *, wgroup_id_t, void *);
+
+
+/* Modifie les conditions d'attente des fins d'exécutions. */
+void g_work_queue_set_extra_wait_callback(GWorkQueue *, wgroup_id_t, wait_for_incoming_works_cb, void *);
+
+/* Force un réveil d'une attente en cours pour la confirmer. */
+void g_work_queue_wake_up_waiters(GWorkQueue *, wgroup_id_t);
+
+
#endif /* _GLIBEXT_DELAYED_H */