diff options
Diffstat (limited to 'src/debug/debugger.c')
-rw-r--r-- | src/debug/debugger.c | 1551 |
1 files changed, 1493 insertions, 58 deletions
diff --git a/src/debug/debugger.c b/src/debug/debugger.c index 331adc6..0dfe315 100644 --- a/src/debug/debugger.c +++ b/src/debug/debugger.c @@ -23,27 +23,70 @@ #include "debugger.h" + +#include <assert.h> +#include <inttypes.h> +#include <malloc.h> +#include <stdlib.h> + + +#include <i18n.h> + + #include "debugger-int.h" -#include "jdwp/debugger.h" -#include "remgdb/gdb.h" +#include "../common/sort.h" #include "../glibext/chrysamarshal.h" +#include "../gui/panels/log.h" #include "../plugins/pglist.h" +/* ---------------------------- TRONC COMMUN DE DEBOGAGE ---------------------------- */ + + /* Initialise la classe de base des débogueurs. */ static void g_binary_debugger_class_init(GBinaryDebuggerClass *); /* Initialise une instance de base d'un débogueur. */ static void g_binary_debugger_init(GBinaryDebugger *); +/* Supprime toutes les références externes. */ +static void g_binary_debugger_dispose(GBinaryDebugger *); + +/* Procède à la libération totale de la mémoire. */ +static void g_binary_debugger_finalize(GBinaryDebugger *); + + + +/* --------------------------- GESTION DES POINTS D'ARRET --------------------------- */ + + +/* Active un point d'arrêt à un emplacement de mémoire donné. */ +static bool g_binary_debugger_insert_memory_breakpoint(GBinaryDebugger *, virt_t, RawBpOrigin, dbg_thread_id_t, virt_t); + +/* Désactive un point d'arrêt à un emplacement de mémoire donné. */ +static bool g_binary_debugger_remove_memory_breakpoint(GBinaryDebugger *, virt_t); + +/* Sème des points d'arrêt sur les instructions suivantes. */ +static bool g_binary_debugger_spread_breakpoints(GBinaryDebugger *, dbg_thread_id_t, virt_t, RawBpOrigin, bool); + +/* Retire tous les points d'arrêt issus d'un adresse. */ +static void g_binary_debugger_remove_same_breakpoints(GBinaryDebugger *, dbg_thread_id_t, virt_t, RawBpOrigin); + +/* Met à jour les points d'arrêt suite à un arrêt. */ +static bool g_binary_debugger_update_breakpoints_on_stop(GBinaryDebugger *, dbg_thread_id_t, virt_t); + + + +/* ---------------------------------------------------------------------------------- */ +/* TRONC COMMUN DE DEBOGAGE */ +/* ---------------------------------------------------------------------------------- */ /* Indique le type définit pour une ligne de représentation. */ G_DEFINE_TYPE(GBinaryDebugger, g_binary_debugger, G_TYPE_OBJECT); - /****************************************************************************** * * * Paramètres : klass = classe à initialiser. * @@ -58,13 +101,46 @@ G_DEFINE_TYPE(GBinaryDebugger, g_binary_debugger, G_TYPE_OBJECT); static void g_binary_debugger_class_init(GBinaryDebuggerClass *klass) { - g_signal_new("debugger-stopped", + GObjectClass *object; /* Autre version de la classe */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_binary_debugger_dispose; + object->finalize = (GObjectFinalizeFunc)g_binary_debugger_finalize; + + g_signal_new("signaled", + G_TYPE_BINARY_DEBUGGER, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GBinaryDebuggerClass, signaled), + NULL, NULL, + g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, 1, G_TYPE_INT); + + g_signal_new("exited", G_TYPE_BINARY_DEBUGGER, G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET(GBinaryDebuggerClass, debugger_stopped), + G_STRUCT_OFFSET(GBinaryDebuggerClass, exited), NULL, NULL, - g_cclosure_user_marshal_VOID__UINT64_UINT64, - G_TYPE_NONE, 2, G_TYPE_UINT64, G_TYPE_UINT64); + g_cclosure_user_marshal_VOID__INT_INT, + G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT); + + g_signal_new("terminated", + G_TYPE_BINARY_DEBUGGER, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GBinaryDebuggerClass, terminated), + NULL, NULL, + g_cclosure_user_marshal_VOID__INT_INT, + G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT); + + g_signal_new("stopped", + G_TYPE_BINARY_DEBUGGER, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GBinaryDebuggerClass, stopped), + NULL, NULL, + g_cclosure_user_marshal_VOID__UINT64, + G_TYPE_NONE, 1, G_TYPE_UINT64); + + g_signal_new("halted", G_TYPE_BINARY_DEBUGGER, @@ -74,6 +150,18 @@ static void g_binary_debugger_class_init(GBinaryDebuggerClass *klass) g_cclosure_user_marshal_VOID__INT_UINT64_INT, G_TYPE_NONE, 3, G_TYPE_INT, G_TYPE_UINT64, G_TYPE_INT); + + + + + g_signal_new("mem-bp-handled", + G_TYPE_BINARY_DEBUGGER, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GBinaryDebuggerClass, mem_bp_handled), + NULL, NULL, + g_cclosure_user_marshal_VOID__INT_UINT64_INT, + G_TYPE_NONE, 2, G_TYPE_BOOLEAN, G_TYPE_UINT64); + } @@ -91,51 +179,64 @@ static void g_binary_debugger_class_init(GBinaryDebuggerClass *klass) static void g_binary_debugger_init(GBinaryDebugger *debugger) { + g_rw_lock_init(&debugger->bp_lock); } /****************************************************************************** * * -* Paramètres : type = type de débigueur choisi pour l'opération. * -* binary = binaire devant être débogué. * +* Paramètres : debugger = instance d'objet GLib à traiter. * * * -* Description : Crée un nouveau débogueur. * +* Description : Supprime toutes les références externes. * * * -* Retour : Composant GObject mis en place ou NULL. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -GBinaryDebugger *g_new_binary_debugger(DebuggerType type, GLoadedBinary *binary) +static void g_binary_debugger_dispose(GBinaryDebugger *debugger) { - GBinaryDebugger *result; + g_object_unref(G_OBJECT(debugger->binary)); - switch (type) - { - case DGT_JDWP: - result = g_java_debugger_new(binary, NULL); - break; + g_rw_lock_clear(&debugger->bp_lock); - case DGT_REMOTE_GDB: - result = g_gdb_debugger_new(binary, NULL); - break; + G_OBJECT_CLASS(g_binary_debugger_parent_class)->dispose(G_OBJECT(debugger)); - default: - result = NULL; - break; +} - } - if (result != NULL) - result->binary = binary; +/****************************************************************************** +* * +* Paramètres : debugger = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ - return result; +static void g_binary_debugger_finalize(GBinaryDebugger *debugger) +{ + size_t i; /* Boucle de parcours */ + + for (i = 0; i < debugger->bp_count; i++) + fini_raw_breakpoint(debugger->bpoints[i]); + + if (debugger->bpoints != NULL) + free(debugger->bpoints); + + G_OBJECT_CLASS(g_binary_debugger_parent_class)->finalize(G_OBJECT(debugger)); } + + + /****************************************************************************** * * * Paramètres : debugger = débogueur à manipuler ici. * @@ -196,11 +297,13 @@ void g_binary_debugger_run(GBinaryDebugger *debugger) } + + /****************************************************************************** * * * Paramètres : debugger = débogueur à manipuler ici. * * * -* Description : Reprend une procédure de débogage. * +* Description : Tue une procédure de débogage. * * * * Retour : - * * * @@ -208,18 +311,37 @@ void g_binary_debugger_run(GBinaryDebugger *debugger) * * ******************************************************************************/ -void g_binary_debugger_resume(GBinaryDebugger *debugger) +void g_binary_debugger_kill(GBinaryDebugger *debugger) { - debugger->resume(debugger); + debugger->kill(debugger); } + + + + + + + + + +/* ------------------- MANIPULATION DES DIFFERENTS THREADS ACTIFS ------------------- */ + + + +/* ---------------------------------------------------------------------------------- */ +/* MANIPULATION DES DIFFERENTS THREADS ACTIFS */ +/* ---------------------------------------------------------------------------------- */ + + /****************************************************************************** * * -* Paramètres : debugger = débogueur à manipuler ici. * +* Paramètres : list = descriptions à supprimer de la mémoire. * +* count = taille de cette liste. * * * -* Description : Tue une procédure de débogage. * +* Description : Libère la mémoire d'une liste de threads actifs. * * * * Retour : - * * * @@ -227,9 +349,15 @@ void g_binary_debugger_resume(GBinaryDebugger *debugger) * * ******************************************************************************/ -void g_binary_debugger_kill(GBinaryDebugger *debugger) +void delete_dbg_thread_desc(dbg_thread_desc *list, size_t count) { - debugger->kill(debugger); + size_t i; /* Boucle de parcours */ + + for (i = 0; i < count; i++) + free(list[i].name); + + if (list != NULL) + free(list); } @@ -242,52 +370,932 @@ void g_binary_debugger_kill(GBinaryDebugger *debugger) * * * Description : Fournit les identifiants de tous les threads actifs. * * * -* Retour : Liste des threads décomptés. * +* Retour : Liste des threads décomptés, à libérer de la mémoire ensuite.* * * * Remarques : - * * * ******************************************************************************/ -pid_t *g_binary_debugger_list_all_threads(GBinaryDebugger *debugger, char ***names, size_t *count) +dbg_thread_desc *g_binary_debugger_list_all_threads(GBinaryDebugger *debugger, size_t *count) { - pid_t *result; /* Liste à retourner */ + dbg_thread_desc *result; /* Liste à retourner */ - if (debugger->all_threads != NULL) - result = debugger->all_threads(debugger, names, count); - else - result = NULL; + *count = 0; + + result = G_BINARY_DEBUGGER_GET_CLASS(debugger)->all_threads(debugger, count); + + if (result != NULL) + { + int cmp_dbg_thread_desc(const dbg_thread_desc *a, const dbg_thread_desc *b) + { + int status; /* Bilan à retourner */ + + if (a->id < b->id) + status = -1; + + if (a->id > b->id) + status = 1; + + else + status = -1; + + return status; + + } + + qsort(result, *count, sizeof(dbg_thread_desc), (__compar_fn_t)cmp_dbg_thread_desc); + + } return result; } + + + + + + + + +/* ---------------------------------------------------------------------------------- */ +/* ENTREES / SORTIES BASIQUES */ +/* ---------------------------------------------------------------------------------- */ + + /****************************************************************************** * * -* Paramètres : debugger = instance du module de débogage chargé. * -* thread = thread concerné par l'analyse. * -* count = nombre de frames en place. [OUT] * +* Paramètres : debugger = débogueur à relancer. * +* addr = emplacement en mémoire à venir consulter. * +* value = emplacement de la valeur lue à conserver. [OUT] * +* * +* Description : Lit une valeur de 8 bits à une adresse arbitraire. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_read_memory_u8(GBinaryDebugger *debugger, virt_t addr, uint8_t *value) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->read_mem(debugger, addr, 8, value); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à relancer. * +* addr = emplacement en mémoire à venir consulter. * +* value = emplacement de la valeur lue à conserver. [OUT] * +* * +* Description : Lit une valeur de 16 bits à une adresse arbitraire. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_read_memory_u16(GBinaryDebugger *debugger, virt_t addr, uint16_t *value) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->read_mem(debugger, addr, 16, value); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à relancer. * +* addr = emplacement en mémoire à venir consulter. * +* value = emplacement de la valeur lue à conserver. [OUT] * +* * +* Description : Lit une valeur de 32 bits à une adresse arbitraire. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_read_memory_u32(GBinaryDebugger *debugger, virt_t addr, uint32_t *value) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->read_mem(debugger, addr, 32, value); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à relancer. * +* addr = emplacement en mémoire à venir consulter. * +* value = emplacement de la valeur lue à conserver. [OUT] * +* * +* Description : Lit une valeur de 64 bits à une adresse arbitraire. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_read_memory_u64(GBinaryDebugger *debugger, virt_t addr, uint64_t *value) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->read_mem(debugger, addr, 64, value); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à relancer. * +* addr = emplacement en mémoire à venir consulter. * +* value = emplacement de la valeur lue à conserver. [OUT] * +* len = taille attendue de la valeur en octets. * +* * +* Description : Lit une valeur de taille quelconque à une adresse arbitraire.* +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_read_memory_data(GBinaryDebugger *debugger, virt_t addr, uint8_t *value, size_t len) +{ + bool result; /* Bilan à retourner */ + size_t iter; /* Tête de lecture / écriture */ + size_t remaining; /* Quantité restant à replacer */ + + result = true; + + iter = 0; + remaining = len; + + while (result && remaining > 0) + { + if (remaining >= 8) + { + result = g_binary_debugger_read_memory_u64(debugger, addr + iter, (uint64_t *)&value[iter]); + + iter += 8; + remaining -= 8; + + } + + else if (remaining >= 4) + { + result = g_binary_debugger_read_memory_u32(debugger, addr + iter, (uint32_t *)(&value[iter])); + + iter += 4; + remaining -= 4; + + } + + else if (remaining >= 2) + { + result = g_binary_debugger_read_memory_u16(debugger, addr + iter, (uint16_t *)&value[iter]); + + iter += 2; + remaining -= 2; + + } + + else if (remaining >= 1) + { + result = g_binary_debugger_read_memory_u8(debugger, addr + iter, (uint8_t *)&value[iter]); + + iter += 1; + remaining -= 1; + + } + + else + assert(false); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à relancer. * +* addr = emplacement en mémoire à venir modifier. * +* value = emplacement de la valeur à inscrire. * +* * +* Description : Ecrit une valeur de 8 bits à une adresse arbitraire. * * * -* Description : Fournit la liste des frames courantes d'un thread donné. * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_write_memory_u8(GBinaryDebugger *debugger, virt_t addr, const uint8_t *value) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->write_mem(debugger, addr, 8, value); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à relancer. * +* addr = emplacement en mémoire à venir modifier. * +* value = emplacement de la valeur à inscrire. * +* * +* Description : Ecrit une valeur de 16 bits à une adresse arbitraire. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_write_memory_u16(GBinaryDebugger *debugger, virt_t addr, const uint16_t *value) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->write_mem(debugger, addr, 16, value); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à relancer. * +* addr = emplacement en mémoire à venir modifier. * +* value = emplacement de la valeur à inscrire. * +* * +* Description : Ecrit une valeur de 32 bits à une adresse arbitraire. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_write_memory_u32(GBinaryDebugger *debugger, virt_t addr, const uint32_t *value) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->write_mem(debugger, addr, 32, value); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à relancer. * +* addr = emplacement en mémoire à venir modifier. * +* value = emplacement de la valeur à inscrire. * +* * +* Description : Ecrit une valeur de 64 bits à une adresse arbitraire. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_write_memory_u64(GBinaryDebugger *debugger, virt_t addr, const uint64_t *value) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->write_mem(debugger, addr, 64, value); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à relancer. * +* addr = emplacement en mémoire à venir modifier. * +* value = emplacement de la valeur à incrire. * +* len = taille de la valeur fournie en octets. * +* * +* Description : Ecrit une valeur de taille quelconque à une adresse donnée. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_write_memory_data(GBinaryDebugger *debugger, virt_t addr, const uint8_t *value, size_t len) +{ + bool result; /* Bilan à retourner */ + size_t iter; /* Tête de lecture / écriture */ + size_t remaining; /* Quantité restant à replacer */ + + result = true; + + iter = 0; + remaining = len; + + while (result && remaining > 0) + { + if (remaining >= 8) + { + result = g_binary_debugger_write_memory_u64(debugger, addr + iter, (uint64_t *)&value[iter]); + + iter += 8; + remaining -= 8; + + } + + else if (remaining >= 4) + { + result = g_binary_debugger_write_memory_u32(debugger, addr + iter, (uint32_t *)&value[iter]); + + iter += 4; + remaining -= 4; + + } + + else if (remaining >= 2) + { + result = g_binary_debugger_write_memory_u16(debugger, addr + iter, (uint16_t *)&value[iter]); + + iter += 2; + remaining -= 2; + + } + + else if (remaining >= 1) + { + result = g_binary_debugger_write_memory_u8(debugger, addr + iter, (uint8_t *)&value[iter]); + + iter += 1; + remaining -= 1; + + } + + else + assert(false); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à consulter. * +* group = éventuel groupe de registres ciblé ou NULL. * +* count = nombre d'éléments dans la liste de noms. [OUT] * +* * +* Description : Liste l'ensemble des registres appartenant à un groupe. * +* * +* Retour : Liste de noms à libérer de la mémoire après utilisation. * +* * +* Remarques : - * +* * +******************************************************************************/ + +char **g_binary_debugger_get_register_names(const GBinaryDebugger *debugger, const char *group, size_t *count) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->get_reg_names(debugger, group, count); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à consulter. * +* name = désignation du registre visé. * +* * +* Description : Indique la taille associée à un registre donné. * * * -* Retour : Liste des frames trouvées. * +* Retour : Taille en bits, ou 0 si le registre n'a pas été trouvé. * * * * Remarques : - * * * ******************************************************************************/ -dbg_frame_t *g_binary_debugger_get_frames_stack(GBinaryDebugger *debugger, pid_t thread, size_t *count) +unsigned int g_binary_debugger_get_register_size(const GBinaryDebugger *debugger, const char *name) { - dbg_frame_t *result; /* Liste à retourner */ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->get_reg_size(debugger, name); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à consulter. * +* reg = désignation humaine du register à consulter. * +* value = emplacement de la valeur lue à conserver. [OUT] * +* * +* Description : Lit une valeur de 8 bits à partir d'un registre. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_read_register_u8(GBinaryDebugger *debugger, const char *reg, uint8_t *value) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->read_reg(debugger, reg, 8, value); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à consulter. * +* reg = désignation humaine du register à consulter. * +* value = emplacement de la valeur lue à conserver. [OUT] * +* * +* Description : Lit une valeur de 16 bits à partir d'un registre. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_read_register_u16(GBinaryDebugger *debugger, const char *reg, uint16_t *value) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->read_reg(debugger, reg, 16, value); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à consulter. * +* reg = désignation humaine du register à consulter. * +* value = emplacement de la valeur lue à conserver. [OUT] * +* * +* Description : Lit une valeur de 32 bits à partir d'un registre. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_read_register_u32(GBinaryDebugger *debugger, const char *reg, uint32_t *value) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->read_reg(debugger, reg, 32, value); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à consulter. * +* reg = désignation humaine du register à consulter. * +* value = emplacement de la valeur lue à conserver. [OUT] * +* * +* Description : Lit une valeur de 64 bits à partir d'un registre. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_read_register_u64(GBinaryDebugger *debugger, const char *reg, uint64_t *value) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->read_reg(debugger, reg, 64, value); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à manipuler. * +* reg = désignation humaine du register à consulter. * +* value = emplacement de la valeur à écrire. * +* * +* Description : Ecrit une valeur de 8 bits dans un registre. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_write_register_u8(GBinaryDebugger *debugger, const char *reg, const uint8_t *value) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->write_reg(debugger, reg, 8, value); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à manipuler. * +* reg = désignation humaine du register à consulter. * +* value = emplacement de la valeur à écrire. * +* * +* Description : Ecrit une valeur de 16 bits dans un registre. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_write_register_u16(GBinaryDebugger *debugger, const char *reg, const uint16_t *value) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->write_reg(debugger, reg, 16, value); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à manipuler. * +* reg = désignation humaine du register à consulter. * +* value = emplacement de la valeur à écrire. * +* * +* Description : Ecrit une valeur de 32 bits dans un registre. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_write_register_u32(GBinaryDebugger *debugger, const char *reg, const uint32_t *value) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->write_reg(debugger, reg, 32, value); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à manipuler. * +* reg = désignation humaine du register à consulter. * +* value = emplacement de la valeur à écrire. * +* * +* Description : Ecrit une valeur de 64 bits dans un registre. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_write_register_u64(GBinaryDebugger *debugger, const char *reg, const uint64_t *value) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->write_reg(debugger, reg, 64, value); + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* MANIPULATION DE L'ETAT COURANT */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à consulter. * +* pc = adresse de l'instruction courante. [OUT] * +* * +* Description : Détermine le point d'exécution courant. * +* * +* Retour : Bilan de la récupération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_get_current_pc(GBinaryDebugger *debugger, virt_t *pc) +{ + bool result; /* Bilan à retourner */ + + result = G_BINARY_DEBUGGER_GET_CLASS(debugger)->get_current_pc(debugger, pc); + + if (!result) + log_variadic_message(LMT_WARNING, "Unable to get the current PC!"); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à consulter. * +* pc = adresse de l'instruction de retour d'appel. [OUT] * +* * +* Description : Détermine l'adresse du premier retour d'appel. * +* * +* Retour : Bilan de la récupération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_get_return_pc(GBinaryDebugger *debugger, virt_t *pc) +{ + bool result; /* Bilan à retourner */ + virt_t *callstack; /* Pile d'appels courante */ + size_t size; /* Hauteur de cette pile */ + + result = g_binary_debugger_get_call_stack(debugger, &callstack, &size); + + if (result && size > 0) + *pc = callstack[size - 1]; + + if (callstack != NULL) + free(callstack); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à consulter. * +* over = indique si les appels doivent être sautés ou non. * +* count = nombre d'adresses identifiées. * +* * +* Description : Détermine les prochaines probables instructions exécutées. * +* * +* Retour : Liste d'adresses à libérer de la mémoire après usage. * +* * +* Remarques : - * +* * +******************************************************************************/ + +virt_t *g_binary_debugger_get_next_pcs(GBinaryDebugger *debugger, virt_t pc, bool over, size_t *count) +{ + virt_t *result; /* Liste à retourner */ + GArchProcessor *proc; /* Processeur lié au binaire */ + vmpa2t addr; /* Localisation à cibler */ + instr_iter_t *iter; /* Parcours local d'adresses */ + GArchInstruction *instr; /* Instruction correspondante */ + virt_t ret; /* Adresse de retour d'appel */ + instr_link_t *dests; /* Instr. visées par une autre */ + size_t dcount; /* Nombre de liens de dest. */ + size_t i; /* Boucle de parcours */ + const mrange_t *range; /* Emplacement d'instruction */ + + result = NULL; + *count = 0; + + proc = g_loaded_binary_get_processor(debugger->binary); + + init_vmpa(&addr, VMPA_NO_PHYSICAL, pc); + iter = g_arch_processor_get_iter_from_address(proc, &addr); + + if (iter != NULL) + { + instr = get_instruction_iterator_current(iter); + + /* Si l'instruction est un retour à l'appelant */ + if (g_arch_instruction_get_flags(instr) & AIF_RETURN_POINT) + { + if (g_binary_debugger_get_return_pc(debugger, &ret)) + { + *count = 1; + result = (virt_t *)malloc(sizeof(virt_t)); + + result[0] = ret; + + } + + } + + /* Sinon on se penche sur ses destinations */ + else + { + g_arch_instruction_rlock_dest(instr); + + dcount = g_arch_instruction_get_destinations(instr, &dests); + + for (i = 0; i < dcount; i++) + switch (dests[i].type) + { + case ILT_EXEC_FLOW: + case ILT_JUMP: + case ILT_CASE_JUMP: + case ILT_JUMP_IF_TRUE: + case ILT_JUMP_IF_FALSE: + case ILT_LOOP: + + (*count)++; + result = (virt_t *)realloc(result, *count * sizeof(virt_t)); + + range = g_arch_instruction_get_range(dests[i].linked); + + result[*count - 1] = get_virt_addr(get_mrange_addr(range)); + break; + + case ILT_CALL: + + if (!over) + { + (*count)++; + result = (virt_t *)realloc(result, *count * sizeof(virt_t)); + + range = g_arch_instruction_get_range(dests[i].linked); + + result[*count - 1] = get_virt_addr(get_mrange_addr(range)); + + } + + break; + + default: + break; + + } + + g_arch_instruction_runlock_dest(instr); + + /* Si tout ça ne donne rien, on se rabat sur l'instruction suivante par défaut */ + if (*count == 0) + { + g_object_unref(G_OBJECT(instr)); + + instr = get_instruction_iterator_next(iter); + + if (instr != NULL) + { + *count = 1; + result = (virt_t *)malloc(sizeof(virt_t)); + + range = g_arch_instruction_get_range(instr); + + result[0] = get_virt_addr(get_mrange_addr(range)); + + } + + } + + } + + if (instr != NULL) + g_object_unref(G_OBJECT(instr)); + + delete_instruction_iterator(iter); + + } + + g_object_unref(G_OBJECT(proc)); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à consulter. * +* callstack = pile d'appels reconstituée. [OUT] * +* size = taille de cette pile. [OUT] * +* * +* Description : Remonte la pile d'appels jusqu'au point courant. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_get_call_stack(GBinaryDebugger *debugger, virt_t **callstack, size_t *size) +{ + bool result; /* Bilan à retourner */ + + *callstack = NULL; + *size = 0; + + result = G_BINARY_DEBUGGER_GET_CLASS(debugger)->get_call_stack(debugger, callstack, size); + + if (!result) + { + if (*callstack != NULL) + free(*callstack); + + *callstack = NULL; + *size = 0; + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à manipuler ici. * +* pc = adresse de l'instruction courante. * +* * +* Description : Réagit à un arrêt du flot d'exécution. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void on_binary_debugger_stopped(GBinaryDebugger *debugger, virt_t pc) +{ + pid_t tid; /* Identifiant du thread */ + bool auto_resume; /* Poursuite automatique ? */ + + tid = 1; // FIXME + + auto_resume = g_binary_debugger_update_breakpoints_on_stop(debugger, tid, pc); + + if (!auto_resume) + g_signal_emit_by_name(debugger, "stopped", pc); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à manipuler ici. * +* pid = éventuel identifiant de processus concerné ou -1. * +* * +* Description : Réagit à la fin de l'opération de débogage. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void on_binary_debugger_finished(GBinaryDebugger *debugger, pid_t pid) +{ + + /* TODO : libérer de la mémoire tous les BP */ + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* GESTION DES POINTS D'ARRET */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à manipuler ici. * +* addr = emplacement du point mémoire à traiter. * +* origin = origine de la création du point d'arrêt. * +* tid = identifiant du thread concerné. * +* previous = éventuelle adresse précédent celle du point. * +* * +* Description : Active un point d'arrêt à un emplacement de mémoire donné. * +* * +* Retour : true si la demande a été prise en compte, false sinon. * +* * +* Remarques : L'accès à la liste doit être placée sous la protection de * +* l'appelant. * +* * +******************************************************************************/ + +static bool g_binary_debugger_insert_memory_breakpoint(GBinaryDebugger *debugger, virt_t addr, RawBpOrigin origin, dbg_thread_id_t tid, virt_t previous) +{ + bool result; /* Bilan à retourner */ + size_t index; /* Indice de ce point d'arrêt */ + bool found; /* Présence d'un point d'arrêt */ + raw_breakpoint *bp; /* Point d'arrêt à constituer */ + + result = false; + + found = bsearch_index(&addr, debugger->bpoints, debugger->bp_count, sizeof(raw_breakpoint *), + (__compar_fn_t)compare_raw_breakpoint_with_addr, &index); + + if (found) + { + bp = debugger->bpoints[index]; + result = true; + } - if (debugger->frames_stack != NULL) - result = debugger->frames_stack(debugger, thread, count); else { - *count = 0; - result = NULL; + bp = G_BINARY_DEBUGGER_GET_CLASS(debugger)->enable_bp(debugger, addr); + + if (bp != NULL) + { + debugger->bpoints = (raw_breakpoint **)qinsert(debugger->bpoints, &debugger->bp_count, + sizeof(raw_breakpoint *), + (__compar_fn_t)compare_raw_breakpoints, &bp); + result = true; + } + } + if (result) + set_raw_breakpoint_origin(bp, origin, tid, previous); + return result; } @@ -296,18 +1304,445 @@ dbg_frame_t *g_binary_debugger_get_frames_stack(GBinaryDebugger *debugger, pid_t /****************************************************************************** * * * Paramètres : debugger = débogueur à manipuler ici. * -* count = nombre de transmissions effetuées. * +* index = indice du point à supprimer. * +* * +* Description : Désactive un point d'arrêt à un emplacement de mémoire donné.* +* * +* Retour : true si la demande a été prise en compte, false sinon. * * * -* Description : Fournit la valeur des registres de l'architecture. * +* Remarques : L'accès à la liste doit être placée sous la protection de * +* l'appelant. * * * -* Retour : Tableau de valeurs transmises à libérer de la mémoire / NULL.* +******************************************************************************/ + +static bool g_binary_debugger_remove_memory_breakpoint(GBinaryDebugger *debugger, size_t index) +{ + bool result; /* Bilan à retourner */ + raw_breakpoint *bp; /* Point d'arrêt à manipuler */ + + result = false; + + assert(index < debugger->bp_count); + + bp = debugger->bpoints[index]; + + result = G_BINARY_DEBUGGER_GET_CLASS(debugger)->disable_bp(debugger, bp); + + if (result) + { + debugger->bpoints = (raw_breakpoint **)_qdelete(debugger->bpoints, &debugger->bp_count, + sizeof(raw_breakpoint *), index); + + fini_raw_breakpoint(bp); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à relancer. * +* tid = identifiant du thread concerné. * +* pc = adresse de l'instruction courante. * +* type = type de point d'arrêt à insérer. * +* over = indique si les appels doivent être sautés ou non. * +* * +* Description : Sème des points d'arrêt sur les instructions suivantes. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_binary_debugger_spread_breakpoints(GBinaryDebugger *debugger, dbg_thread_id_t tid, virt_t pc, RawBpOrigin type, bool over) +{ + bool result; /* Bilan à retourner */ + virt_t *next_list; /* Liste de points d'arrêts */ + size_t next_count; /* Taille de cette liste */ + GArchProcessor *proc; /* Processeur lié au binaire */ + size_t i; /* Boucle de parcours */ + vmpa2t addr; /* Format d'adresse complet */ + GArchInstruction *instr; /* Instruction ciblée */ + bool valid; /* Point d'arrêt pertinent ? */ + const char *encoding; /* Encodage de l'instruction */ + + result = true; + + next_list = g_binary_debugger_get_next_pcs(debugger, pc, over, &next_count); + + if (next_count == 0) + log_variadic_message(LMT_WARNING, "No instruction found to break after 0x%" PRIx64, pc); + + proc = g_loaded_binary_get_processor(debugger->binary); + + /** + * Le verrou sur la liste des points est normalement déjà posé. + */ + + for (i = 0; i < next_count && result; i++) + { + /** + * Des données peuvent suivre du code (typiquement en ARM). + * On réalise une validation minimale au préalable donc. + */ + + init_vmpa(&addr, VMPA_NO_PHYSICAL, next_list[i]); + instr = g_arch_processor_find_instr_by_address(proc, &addr); + + if (instr == NULL) + valid = false; + + else + { + encoding = g_arch_instruction_get_encoding(instr); + + valid = strcmp(encoding, _("String")) != 0 + && strcmp(encoding, _("Raw")) != 0 + && strcmp(encoding, _("Undefined")) != 0; + + g_object_unref(G_OBJECT(instr)); + + } + + if (valid) + result = g_binary_debugger_insert_memory_breakpoint(debugger, next_list[i], type, tid, pc); + + } + + if (next_list != NULL) + free(next_list); + + g_object_unref(G_OBJECT(proc)); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à relancer. * +* tid = identifiant du thread concerné. * +* prev = adresse d'instruction qui a conduit à des poses. * +* type = type de point d'arrêt à insérer. * +* * +* Description : Retire tous les points d'arrêt issus d'un adresse. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_binary_debugger_remove_same_breakpoints(GBinaryDebugger *debugger, dbg_thread_id_t tid, virt_t prev, RawBpOrigin type) +{ + size_t i; /* Boucle de parcours */ + raw_breakpoint *bp; /* Confort de l'accès rapide */ + + /** + * Le verrou sur la liste des points est normalement déjà posé. + */ + + for (i = 0; i < debugger->bp_count; i++) + { + bp = debugger->bpoints[i]; + + if (has_raw_breakpoint_previous_address(bp, type, tid, prev)) + unset_raw_breakpoint_origin(bp, type, tid); + + } + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à manipuler ici. * +* tid = identifiant du thread concerné. * +* pc = adresse de l'instruction courante. * +* * +* Description : Met à jour les points d'arrêt suite à un arrêt. * +* * +* Retour : true si l'exécution a été relancée automatiquement. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_binary_debugger_update_breakpoints_on_stop(GBinaryDebugger *debugger, dbg_thread_id_t tid, virt_t pc) +{ + bool result; /* Indication à faire remonter */ + size_t index; /* Indice de ce point d'arrêt */ + bool found; /* Présence d'un point d'arrêt */ + raw_breakpoint *bp; /* Confort de l'accès rapide */ + virt_t previous; /* Adresse d'origine */ + bool status; /* Bilan d'un retrait */ + size_t i; /* Boucle de parcours */ + virt_t addr; /* Localisation d'un point */ + + result = false; + + g_rw_lock_writer_lock(&debugger->bp_lock); + + found = bsearch_index(&pc, debugger->bpoints, debugger->bp_count, sizeof(raw_breakpoint *), + (__compar_fn_t)compare_raw_breakpoint_with_addr, &index); + + if (found) + { + bp = debugger->bpoints[index]; + + /* S'il s'agissait d'un point d'arrêt à usage interne */ + if (has_raw_breakpoint_origin(bp, RBO_INTERNAL, tid)) + { + previous = get_raw_breakpoint_prev_addr(bp); + assert(previous != VMPA_NO_VIRTUAL); + + g_binary_debugger_remove_same_breakpoints(debugger, tid, previous, RBO_INTERNAL); + + assert(!has_raw_breakpoint_origin(bp, RBO_INTERNAL, tid)); + + result = true; + + } + + /* S'il s'agissait d'un arrêt demandé par l'utilisateur */ + if (has_raw_breakpoint_origin(bp, RBO_USER, tid)) + { + status = G_BINARY_DEBUGGER_GET_CLASS(debugger)->disable_bp(debugger, bp); + + if (!status) + log_variadic_message(LMT_ERROR, "Error while removing the breakpoint at 0x%" PRIx64, pc); + + g_binary_debugger_spread_breakpoints(debugger, tid, pc, RBO_INTERNAL, false); + + result = false; + + } + + /* S'il s'agissait d'une progression pas à pas */ + if (has_raw_breakpoint_origin(bp, RBO_STEP, tid)) + { + previous = get_raw_breakpoint_prev_addr(bp); + assert(previous != VMPA_NO_VIRTUAL); + + g_binary_debugger_remove_same_breakpoints(debugger, tid, previous, RBO_STEP); + + assert(!has_raw_breakpoint_origin(bp, RBO_STEP, tid)); + + result = false; + + } + + /* En conclusion, on supprime les points inutiles */ + for (i = 0; i < debugger->bp_count; ) + { + bp = debugger->bpoints[i]; + + if (is_breakpoint_useless(bp)) + { + status = g_binary_debugger_remove_memory_breakpoint(debugger, i); + + if (!status) + { + addr = get_raw_breakpoint_addr(bp); + log_variadic_message(LMT_ERROR, "Error while removing the breakpoint at 0x%" PRIx64, addr); + i++; + } + + } + + else i++; + + } + + } + + g_rw_lock_writer_unlock(&debugger->bp_lock); + + if (result) + G_BINARY_DEBUGGER_GET_CLASS(debugger)->resume(debugger); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à manipuler ici. * +* addr = emplacement du point mémoire à traiter. * +* * +* Description : Ajoute un point d'arrêt basique en mémoire. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_add_memory_breakpoint(GBinaryDebugger *debugger, virt_t addr) +{ + bool result; /* Bilan à retourner */ + dbg_thread_id_t tid; /* Identifiant de thread */ + + tid = 1;/// FIXME + + g_rw_lock_writer_lock(&debugger->bp_lock); + + result = g_binary_debugger_insert_memory_breakpoint(debugger, addr, RBO_USER, tid, VMPA_NO_VIRTUAL); + + g_rw_lock_writer_unlock(&debugger->bp_lock); + + if (result) + g_signal_emit_by_name(debugger, "mem-bp-handled", true, addr); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à manipuler ici. * +* addr = emplacement du point mémoire à traiter. * +* * +* Description : Retire un point d'arrêt basique en mémoire. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_delete_memory_breakpoint(GBinaryDebugger *debugger, virt_t addr) +{ + bool result; /* Bilan à retourner */ + size_t index; /* Indice de ce point d'arrêt */ + raw_breakpoint *bp; /* Confort de l'accès rapide */ + dbg_thread_id_t tid; /* Identifiant de thread */ + + g_rw_lock_writer_lock(&debugger->bp_lock); + + result = bsearch_index(&addr, debugger->bpoints, debugger->bp_count, sizeof(raw_breakpoint *), + (__compar_fn_t)compare_raw_breakpoint_with_addr, &index); + + if (result) + { + bp = debugger->bpoints[index]; + + tid = 1;/// FIXME + + result = has_raw_breakpoint_origin(bp, RBO_USER, tid); + + if (result) + { + unset_raw_breakpoint_origin(bp, RBO_USER, tid); + + if (is_breakpoint_useless(bp)) + result = g_binary_debugger_remove_memory_breakpoint(debugger, index); + + } + + } + + g_rw_lock_writer_unlock(&debugger->bp_lock); + + if (result) + g_signal_emit_by_name(debugger, "mem-bp-handled", false, addr); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* CONTROLE DU FLOT D'EXECUTION */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à redémarrer. * +* * +* Description : Redémarre le processus de débogage. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_restart(GBinaryDebugger *debugger) +{ + return G_BINARY_DEBUGGER_GET_CLASS(debugger)->restart(debugger); + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à relancer. * +* * +* Description : Remet en marche le débogueur courant. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_debugger_resume(GBinaryDebugger *debugger) +{ + bool result; /* Bilan à retourner */ + + result = G_BINARY_DEBUGGER_GET_CLASS(debugger)->resume(debugger); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : debugger = débogueur à relancer. * +* over = indique si les appels doivent être sautés ou non. * +* * +* Description : Relance l'exécution pour une seule instruction. * +* * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -register_value *g_binary_debugger_get_registers(GBinaryDebugger *debugger, size_t *count) +bool g_binary_debugger_stepi(GBinaryDebugger *debugger, bool over) { - return debugger->get_reg_values(debugger, count); + bool result; /* Bilan à retourner */ + virt_t pc; /* Position courante */ + dbg_thread_id_t tid; /* Identifiant de thread */ + + result = g_binary_debugger_get_current_pc(debugger, &pc); + + if (result) + { + tid = 1;/// FIXME + + g_rw_lock_writer_lock(&debugger->bp_lock); + + result = g_binary_debugger_spread_breakpoints(debugger, tid, pc, RBO_STEP, over); + + g_rw_lock_writer_unlock(&debugger->bp_lock); + + } + + if (result) + result = G_BINARY_DEBUGGER_GET_CLASS(debugger)->resume(debugger); + + return result; } |