diff options
Diffstat (limited to 'src/debug')
| -rwxr-xr-x | src/debug/Makefile.am | 6 | ||||
| -rw-r--r-- | src/debug/break-int.h | 64 | ||||
| -rw-r--r-- | src/debug/break.c | 489 | ||||
| -rw-r--r-- | src/debug/break.h | 90 | ||||
| -rw-r--r-- | src/debug/debugger-int.h | 128 | ||||
| -rw-r--r-- | src/debug/debugger.c | 1551 | ||||
| -rw-r--r-- | src/debug/debugger.h | 193 | ||||
| -rw-r--r-- | src/debug/gdbrsp/Makefile.am | 24 | ||||
| -rw-r--r-- | src/debug/gdbrsp/aops.h | 57 | ||||
| -rw-r--r-- | src/debug/gdbrsp/gdb-int.h | 116 | ||||
| -rw-r--r-- | src/debug/gdbrsp/gdb.c | 1524 | ||||
| -rw-r--r-- | src/debug/gdbrsp/gdb.h (renamed from src/debug/remgdb/gdb.h) | 8 | ||||
| -rw-r--r-- | src/debug/gdbrsp/helpers.c (renamed from src/debug/remgdb/helpers.c) | 85 | ||||
| -rw-r--r-- | src/debug/gdbrsp/helpers.h (renamed from src/debug/remgdb/helpers.h) | 11 | ||||
| -rw-r--r-- | src/debug/gdbrsp/helpers_arm.c | 252 | ||||
| -rw-r--r-- | src/debug/gdbrsp/helpers_arm.h | 37 | ||||
| -rw-r--r-- | src/debug/gdbrsp/helpers_arm64.c | 97 | ||||
| -rw-r--r-- | src/debug/gdbrsp/helpers_arm64.h | 40 | ||||
| -rw-r--r-- | src/debug/gdbrsp/packet.c (renamed from src/debug/remgdb/packet.c) | 14 | ||||
| -rw-r--r-- | src/debug/gdbrsp/packet.h (renamed from src/debug/remgdb/packet.h) | 6 | ||||
| -rw-r--r-- | src/debug/gdbrsp/stream-int.h (renamed from src/debug/remgdb/stream-int.h) | 21 | ||||
| -rw-r--r-- | src/debug/gdbrsp/stream.c (renamed from src/debug/remgdb/stream.c) | 310 | ||||
| -rw-r--r-- | src/debug/gdbrsp/stream.h (renamed from src/debug/remgdb/stream.h) | 9 | ||||
| -rw-r--r-- | src/debug/gdbrsp/support.c | 598 | ||||
| -rw-r--r-- | src/debug/gdbrsp/support.h | 73 | ||||
| -rw-r--r-- | src/debug/gdbrsp/target.c | 950 | ||||
| -rw-r--r-- | src/debug/gdbrsp/target.h | 72 | ||||
| -rw-r--r-- | src/debug/gdbrsp/tcp.c (renamed from src/debug/remgdb/tcp.c) | 25 | ||||
| -rw-r--r-- | src/debug/gdbrsp/tcp.h (renamed from src/debug/remgdb/tcp.h) | 9 | ||||
| -rw-r--r-- | src/debug/gdbrsp/utils.c | 351 | ||||
| -rw-r--r-- | src/debug/gdbrsp/utils.h | 58 | ||||
| -rw-r--r-- | src/debug/misc.h | 37 | ||||
| -rw-r--r-- | src/debug/remgdb/Makefile.am | 17 | ||||
| -rw-r--r-- | src/debug/remgdb/gdb.c | 363 | 
34 files changed, 6869 insertions, 816 deletions
| diff --git a/src/debug/Makefile.am b/src/debug/Makefile.am index b2f4685..f955a5a 100755 --- a/src/debug/Makefile.am +++ b/src/debug/Makefile.am @@ -2,16 +2,18 @@  noinst_LTLIBRARIES = libdebug.la  libdebug_la_SOURCES =					\ +	break-int.h							\  	break.h break.c						\  	debugger-int.h						\  	debugger.h debugger.c				\ +	misc.h								\  	packet-int.h						\  	packet.h packet.c					\  	stream-int.h						\  	stream.h stream.c  libdebug_la_LIBADD =					\ -	jdwp/libdebugjdwp.la +	gdbrsp/libdebuggdbrsp.la  libdebug_la_CFLAGS = $(AM_CFLAGS) @@ -20,4 +22,4 @@ AM_CPPFLAGS = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS)  AM_CFLAGS = $(DEBUG_CFLAGS) $(WARNING_FLAGS) $(COMPLIANCE_FLAGS) -SUBDIRS = jdwp remgdb +SUBDIRS = gdbrsp diff --git a/src/debug/break-int.h b/src/debug/break-int.h new file mode 100644 index 0000000..44b3664 --- /dev/null +++ b/src/debug/break-int.h @@ -0,0 +1,64 @@ +/* Chrysalide - Outil d'analyse de fichiers binaires + * break-int.h - prototypes pour la définition générique interne des points d'arrêt + * + * Copyright (C) 2016 Cyrille Bagard + * + *  This file is part of Chrysalide. + * + *  Chrysalide is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 3 of the License, or + *  (at your option) any later version. + * + *  Chrysalide is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with Foobar.  If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _DEBUG_BREAK_INT_H +#define _DEBUG_BREAK_INT_H + + +#include "break.h" + + + +/* Traçabilité des poses */ +typedef struct _bp_source +{ +    RawBpOrigin origin;                     /* Source de la définition     */ + +    /* Si origin != RBO_USER : */ + +    dbg_thread_id_t tid;                    /* Identifiant du thread lié   */ +    virt_t previous;                        /* Arrêt officiel d'origine    */ + +} bp_source; + + +/* Définition d'un point d'arrêt appliqué */ +struct _raw_breakpoint +{ +    virt_t addr;                            /* Adresse d'application       */ + +    union +    { +        bp_source source;                   /* Origine du point d'arrêt    */ +        bp_source *sources;                 /* Origines du point d'arrêt   */ +    }; +    size_t count;                           /* Nombre de ces origines      */ + +}; + + +/* Initialise le coeur d'un point d'arrêt. */ +void init_raw_breakpoint(raw_breakpoint *, virt_t); + + + +#endif  /* _DEBUG_BREAK_INT_H */ diff --git a/src/debug/break.c b/src/debug/break.c index 19eb787..1d4c2d3 100644 --- a/src/debug/break.c +++ b/src/debug/break.c @@ -24,114 +24,20 @@  #include "break.h" +#include <assert.h>  #include <malloc.h> -#include <string.h> -#include "../common/dllist.h" +#include "break-int.h" - -/* --------------------------- GESTION DES POINTS D'ARRET --------------------------- */ - - -/* Propriétés d'un point d'arrêt (instance) */ -struct _GBreakPoint -{ -    GObject parent;                         /* A laisser en premier        */ - -    DL_LIST_ITEM(link);                     /* Maillon de liste chaînée    */ - -    vmpa_t address;                         /* Adresse où s'arrêter        */ - -    bool is_enabled;                        /* Statut d'activité           */ - -}; - -/* Propriétés d'un point d'arrêt (classe) */ -struct _GBreakPointClass -{ -    GObjectClass parent;                    /* A laisser en premier        */ - -    /* Signaux */ - -    void (* changed) (GBreakPoint *); - -}; - - -#define bp_list_add_tail(new, head) dl_list_add_tail(new, head, GBreakPoint, link) -#define bp_list_del(item, head) dl_list_del(item, head, GBreakPoint, link) -#define bp_list_for_each(pos, head) dl_list_for_each(pos, head, GBreakPoint, link) -#define bp_list_for_each_safe(pos, head, next) dl_list_for_each_safe(pos, head, next, GBreakPoint, link) - - -/* Initialise la classe des propriétés d'un point d'arrêt. */ -static void g_break_point_class_init(GBreakPointClass *); - -/* Initialise des propriétés d'un point d'arrêt. */ -static void g_break_point_init(GBreakPoint *); - - - - - - -/* ---------------------------- GROUPE DE POINTS D'ARRET ---------------------------- */ - - -/* Propriétés d'un groupe de points d'arrêt (instance) */ -struct _GBreakGroup -{ -    GObject parent;                         /* A laisser en premier        */ - -    char *name;                             /* Désignation humaine         */ - -    GBreakPoint *points;                    /* Liste de points d'arrêt     */ - -}; - -/* Propriétés d'un groupe de points d'arrêt (classe) */ -struct _GBreakGroupClass -{ -    GObjectClass parent;                    /* A laisser en premier        */ - -    /* Signaux */ - -    void (* added) (GBreakGroup *, GBreakPoint *); -    void (* removed) (GBreakGroup *, GBreakPoint *); - -}; - - -/* Initialise la classe des groupes de points d'arrêt. */ -static void g_break_group_class_init(GBreakGroupClass *); - -/* Initialise un groupe de points d'arrêt. */ -static void g_break_group_init(GBreakGroup *); - - - - - - - - -/* ---------------------------------------------------------------------------------- */ -/*                             GESTION DES POINTS D'ARRET                             */ -/* ---------------------------------------------------------------------------------- */ - - -/* Indique le type défini pour des propriétés d'un point d'arrêt. */ -G_DEFINE_TYPE(GBreakPoint, g_break_point, G_TYPE_OBJECT); - -  /******************************************************************************  *                                                                             * -*  Paramètres  : klass = classe à initialiser.                                * +*  Paramètres  : bp   = point d'arrêt à initialiser.                          * +*                addr = adresse d'action du point d'arrêt.                    *  *                                                                             * -*  Description : Initialise la classe des propriétés d'un point d'arrêt.      * +*  Description : Initialise le coeur d'un point d'arrêt.                      *  *                                                                             *  *  Retour      : -                                                            *  *                                                                             * @@ -139,24 +45,20 @@ G_DEFINE_TYPE(GBreakPoint, g_break_point, G_TYPE_OBJECT);  *                                                                             *  ******************************************************************************/ -static void g_break_point_class_init(GBreakPointClass *klass) +void init_raw_breakpoint(raw_breakpoint *bp, virt_t addr)  { -    g_signal_new("changed", -                 G_TYPE_BREAK_POINT, -                 G_SIGNAL_RUN_LAST, -                 G_STRUCT_OFFSET(GBreakPointClass, changed), -                 NULL, NULL, -                 g_cclosure_marshal_VOID__VOID, -                 G_TYPE_NONE, 0); +    bp->addr = addr; + +    bp->count = 0;  }  /******************************************************************************  *                                                                             * -*  Paramètres  : point = instance à initialiser.                              * +*  Paramètres  : bp = point d'arrêt à traiter.                                *  *                                                                             * -*  Description : Initialise des propriétés d'un point d'arrêt.                * +*  Description : Libère le coeur d'un point d'arrêt.                          *  *                                                                             *  *  Retour      : -                                                            *  *                                                                             * @@ -164,135 +66,133 @@ static void g_break_point_class_init(GBreakPointClass *klass)  *                                                                             *  ******************************************************************************/ -static void g_break_point_init(GBreakPoint *point) +void fini_raw_breakpoint(raw_breakpoint *bp)  { -    DL_LIST_ITEM_INIT(&point->link); +    if (bp->count > 1) +        free(bp->sources); + +    free(bp);  }  /******************************************************************************  *                                                                             * -*  Paramètres  : address = adresse à laquelle s'arrêter.                      * +*  Paramètres  : bp = point d'arrêt à consulter.                              *  *                                                                             * -*  Description : Construit un nouveau point d'arrêt.                          * +*  Description : Indique l'adresse du point d'arrêt dans la mémoire ciblée.   *  *                                                                             * -*  Retour      : Point d'arrêt mis en place ou NULL en cas d'échec.           * +*  Retour      : Adresse associée au point d'arrêt.                           *  *                                                                             *  *  Remarques   : -                                                            *  *                                                                             *  ******************************************************************************/ -GBreakPoint *g_break_point_new(vmpa_t address) +virt_t get_raw_breakpoint_addr(const raw_breakpoint *bp)  { -    GBreakPoint *result;                    /* Adresse à retourner         */ - -    result = g_object_new(G_TYPE_BREAK_POINT, NULL); - -    result->address = address; - -    return result; +    return bp->addr;  } +  /******************************************************************************  *                                                                             * -*  Paramètres  : point = instance à consulter.                                * +*  Paramètres  : bp = point d'arrêt à consulter.                              *  *                                                                             * -*  Description : Fournit l'adresse associée à un point d'arrêt.               * +*  Description : Fournit l'adresse d'origine d'un point d'arrêt de pas à pas. *  *                                                                             * -*  Retour      : Adresse associée.                                            * +*  Retour      : -                                                            *  *                                                                             * -*  Remarques   : -                                                            * +*  Remarques   : Un appel à cette fonction n'est valide que pour un point     * +*                d'arrêt de type RBO_STEP.                                    *  *                                                                             *  ******************************************************************************/ -vmpa_t g_break_point_get_address(const GBreakPoint *point) +virt_t get_raw_breakpoint_prev_addr(const raw_breakpoint *bp)  { -    return point->address; +    virt_t result;                          /* Localisation à retourner    */ +    bool found;                             /* Valide une obtention        */ +    size_t i;                               /* Boucle de parcours          */ -} +    switch (bp->count) +    { +        case 1: +            assert(bp->source.origin == RBO_INTERNAL || bp->source.origin == RBO_STEP); +            result = bp->source.previous; +            break; +        default: +            found = false; -/* ---------------------------------------------------------------------------------- */ -/*                              GROUPE DE POINTS D'ARRET                              */ -/* ---------------------------------------------------------------------------------- */ +            for (i = 0; i < bp->count && !found; i++) +                if (bp->sources[i].origin == RBO_INTERNAL || bp->sources[i].origin == RBO_STEP) +                { +                    result = bp->sources[i].previous; +                    found = true; +                } +            assert(found); -/* Indique le type défini pour les groupes de points d'errêt. */ -G_DEFINE_TYPE(GBreakGroup, g_break_group, G_TYPE_OBJECT); +            break; +    } -/****************************************************************************** -*                                                                             * -*  Paramètres  : klass = classe à initialiser.                                * -*                                                                             * -*  Description : Initialise la classe des groupes de points d'arrêt.          * -*                                                                             * -*  Retour      : -                                                            * -*                                                                             * -*  Remarques   : -                                                            * -*                                                                             * -******************************************************************************/ - -static void g_break_group_class_init(GBreakGroupClass *klass) -{ -    g_signal_new("added", -                 G_TYPE_BREAK_GROUP, -                 G_SIGNAL_RUN_LAST, -                 G_STRUCT_OFFSET(GBreakGroupClass, added), -                 NULL, NULL, -                 g_cclosure_marshal_VOID__OBJECT, -                 G_TYPE_NONE, 1, G_TYPE_BREAK_POINT); - -    g_signal_new("removed", -                 G_TYPE_BREAK_GROUP, -                 G_SIGNAL_RUN_LAST, -                 G_STRUCT_OFFSET(GBreakGroupClass, removed), -                 NULL, NULL, -                 g_cclosure_marshal_VOID__OBJECT, -                 G_TYPE_NONE, 1, G_TYPE_BREAK_POINT); +    return result;  }  /******************************************************************************  *                                                                             * -*  Paramètres  : group = instance à initialiser.                              * +*  Paramètres  : addr = adresse à consulter.                                  * +*                bp   = point d'arrêt à consulter.                            *  *                                                                             * -*  Description : Initialise un groupe de point d'arrêt.                       * +*  Description : Effectue une comparaison entre adresse et point d'arrêt.     *  *                                                                             * -*  Retour      : -                                                            * +*  Retour      : Bilan de la comparaison.                                     *  *                                                                             *  *  Remarques   : -                                                            *  *                                                                             *  ******************************************************************************/ -static void g_break_group_init(GBreakGroup *group) +int compare_raw_breakpoint_with_addr(const virt_t *addr, const raw_breakpoint **bp)  { +    int result;                             /* Bilan à retourner           */ + +    if (*addr < (*bp)->addr) +        result = -1; + +    else if (*addr == (*bp)->addr) +        result = 0; + +    else +        result = 1; + +    return result;  }  /******************************************************************************  *                                                                             * -*  Paramètres  : -                                                            * +*  Paramètres  : a = premier point d'arrêt à consulter.                       * +*                b = second point d'arrêt à consulter.                        *  *                                                                             * -*  Description : Construit un nouveau groupe de points d'arrêt.               * +*  Description : Effectue une comparaison entre deux points d'arrêt.          *  *                                                                             * -*  Retour      : Groupe de points d'arrêt mis en place ou NULL en cas d'échec.* +*  Retour      : Bilan de la comparaison.                                     *  *                                                                             *  *  Remarques   : -                                                            *  *                                                                             *  ******************************************************************************/ -GBreakGroup *g_break_group_new(void) +int compare_raw_breakpoints(const raw_breakpoint **a, const raw_breakpoint **b)  { -    GBreakGroup *result;                    /* Adresse à retourner         */ +    int result;                             /* Bilan à retourner           */ -    result = g_object_new(G_TYPE_BREAK_GROUP, NULL); +    result = compare_raw_breakpoint_with_addr(&(*a)->addr, b);      return result; @@ -301,140 +201,263 @@ GBreakGroup *g_break_group_new(void)  /******************************************************************************  *                                                                             * -*  Paramètres  : group = groupe de points d'arrêt à consulter.                * +*  Paramètres  : bp       = point d'arrêt à manipuler.                        * +*                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 : Fournit la désignation humaine associée à un groupe.         * +*  Description : Enregistre la source d'un point d'arrêt posé.                *  *                                                                             * -*  Retour      : Désignation humaine associée, jamais NULL.                   * +*  Retour      : -                                                            *  *                                                                             *  *  Remarques   : -                                                            *  *                                                                             *  ******************************************************************************/ -const char *g_break_group_get_name(const GBreakGroup *group) +void set_raw_breakpoint_origin(raw_breakpoint *bp, RawBpOrigin origin, dbg_thread_id_t tid, virt_t previous)  { -    return group->name; +#ifndef NDEBUG +    size_t i;                               /* Boucle de parcours          */ +#endif +    bp_source *src;                         /* Source à définir            */ +    bp_source tmp;                          /* Copie temporaire            */ + +#ifndef NDEBUG + +    if (bp->count == 1) +        assert(bp->source.tid != tid || (bp->source.origin & origin) == 0); + +    else +        for (i = 0; i < bp->count; i++) +            if (bp->source.tid == tid) +            { +                assert((bp->sources[i].origin & origin) == 0); +                break; +            } + +#endif + +    bp->count++; + +    switch (bp->count) +    { +        case 1: +            src = &bp->source; +            break; + +        case 2: +            tmp = bp->source; +            bp->sources = (bp_source *)calloc(2, sizeof(bp_source)); +            bp->sources[0] = tmp; +            src = &bp->sources[1]; +            break; + +        default: +            bp->sources = (bp_source *)realloc(bp->sources, bp->count * sizeof(bp_source)); +            src = &bp->sources[bp->count - 1]; +            break; + +    } + +    src->origin = origin; +    src->tid = tid; +    src->previous = previous;  }  /******************************************************************************  *                                                                             * -*  Paramètres  : group = groupe de points d'arrêt à modifier.                 * -*                name  = nouveau nom de scène.                                * +*  Paramètres  : bp       = point d'arrêt à manipuler.                        * +*                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 : Définit la désignation humaine à associer à un groupe.       * +*  Description : Oublie la source d'un point d'arrêt posé.                    *  *                                                                             * -*  Retour      : Désignation humaine associée, voire NULL.                    * +*  Retour      : -                                                            *  *                                                                             *  *  Remarques   : -                                                            *  *                                                                             *  ******************************************************************************/ -void g_break_group_set_name(GBreakGroup *group, const char *name) +void unset_raw_breakpoint_origin(raw_breakpoint *bp, RawBpOrigin origin, dbg_thread_id_t tid)  { -    if (group->name != NULL) -        free(group->name); +    size_t i;                               /* Boucle de parcours #1       */ +    bp_source tmp;                          /* Copie temporaire            */ +#ifndef NDEBUG +    size_t j;                               /* Boucle de parcours #2       */ +#endif -    if (name == NULL) -        group->name = NULL; +    bool has_same_origin(bp_source *src) +    { +        bool result; -    else -        group->name = strdup(name); +        result = (src->origin == origin && src->tid == tid); + +        return result; + +    } + +    switch (bp->count) +    { +        case 1: + +            if (has_same_origin(&bp->source)) +                bp->count = 0; + +            break; + +        case 2: + +            if (has_same_origin(&bp->sources[0])) +            { +                assert(!has_same_origin(&bp->sources[1])); + +                tmp = bp->sources[1]; + +                bp->count = 1; +                free(bp->sources); + +                bp->source = tmp; + +            } + +            else if (has_same_origin(&bp->sources[1])) +            { +                assert(!has_same_origin(&bp->sources[0])); + +                tmp = bp->sources[0]; + +                bp->count = 1; +                free(bp->sources); + +                bp->source = tmp; + +            } + +            break; + +        default: + +            for (i = 0; i < bp->count; i++) +            { +                if (has_same_origin(&bp->sources[i])) +                { +                    if ((i + 1) < bp->count) +                        memmove(&bp->sources[i], &bp->sources[i + 1], (bp->count - i - 1) * sizeof(bp_source)); + +                    bp->sources = (bp_source *)realloc(bp->sources, --bp->count * sizeof(bp_source)); + +#ifndef NDEBUG +                    for (j = i; j < bp->count; j++) +                        assert(!has_same_origin(&bp->sources[j])); +#endif + +                    break; + +                } + +            } + +            break; + +    }  }  /******************************************************************************  *                                                                             * -*  Paramètres  : group = groupe de points d'arrêt à consulter.                * -*                addr  = adresse recherchée dans les points d'arrêt.          * +*  Paramètres  : bp     = point d'arrêt à manipuler.                          * +*                origin = origine de la création du point d'arrêt.            * +*                tid    = identifiant du thread concerné.                     *  *                                                                             * -*  Description : Indique si une adresse donnée est gérée dans un groupe.      * +*  Description : Indique si le point d'arrêt correspond à une source donnée.  *  *                                                                             * -*  Retour      : true si l'adresse est gérée ici, false sinon.                * +*  Retour      : Bilan de l'analyse.                                          *  *                                                                             *  *  Remarques   : -                                                            *  *                                                                             *  ******************************************************************************/ -bool g_break_group_has_address(const GBreakGroup *group, vmpa_t addr) +bool has_raw_breakpoint_origin(const raw_breakpoint *bp, RawBpOrigin origin, dbg_thread_id_t tid)  { -    GBreakPoint *iter;                      /* Boucle de parcours          */ +    bool result;                            /* Conclusion à retourner      */ +    size_t i;                               /* Boucle de parcours          */ + +    if (bp->count == 1) +        result = (bp->source.tid == tid && (bp->source.origin & origin) != 0); + +    else +    { +        result = false; + +        for (i = 0; i < bp->count && !result; i++) +            result = (bp->sources[i].tid == tid && (bp->sources[i].origin & origin) != 0); -    bp_list_for_each(iter, group->points) -        if (g_break_point_get_address(iter) == addr) -            return true; +    } -    return false; +    return result;  }  /******************************************************************************  *                                                                             * -*  Paramètres  : group = groupe de points d'arrêt à modifier.                 * -*                addr  = adresse mémoire à faire basculer.                    * +*  Paramètres  : bp     = point d'arrêt à manipuler.                          * +*                origin = origine de la création du point d'arrêt.            * +*                tid    = identifiant du thread concerné.                     * +*                prev   = adresse d'instruction qui a conduit à des poses.    *  *                                                                             * -*  Description : Ajoute ou supprime un point d'arrêt dans un groupe.          * +*  Description : Indique si le point d'arrêt correspond à une origine donnée. *  *                                                                             * -*  Retour      : -                                                            * +*  Retour      : Bilan de l'analyse.                                          *  *                                                                             *  *  Remarques   : -                                                            *  *                                                                             *  ******************************************************************************/ -void g_break_group_toggle_breakpoint(GBreakGroup *group, vmpa_t addr) +bool has_raw_breakpoint_previous_address(const raw_breakpoint *bp, RawBpOrigin origin, dbg_thread_id_t tid, virt_t prev)  { -    GBreakPoint *iter;                      /* Boucle de parcours          */ -    GBreakPoint *next;                      /* Prochain point de passage   */ +    bool result;                            /* Conclusion à retourner      */ +    size_t i;                               /* Boucle de parcours          */ -    /* Suppression d'un éventuel existant */ +    if (bp->count == 1) +        result = (bp->source.tid == tid && (bp->source.origin & origin) != 0 && bp->source.previous == prev); -    bp_list_for_each_safe(iter, &group->points, next) -        if (g_break_point_get_address(iter) == addr) -        { -            g_signal_emit_by_name(group, "removed", iter); - -            bp_list_del(iter, &group->points); -            g_object_unref(G_OBJECT(iter)); - -            return; - -        } - -    /* Si non trouvé, ajout du nouveau point */ +    else +    { +        result = false; -    iter = g_break_point_new(addr); +        for (i = 0; i < bp->count && !result; i++) +            result = (bp->sources[i].tid == tid && (bp->sources[i].origin & origin) != 0 && bp->sources[i].previous == prev); -    bp_list_add_tail(iter, &group->points); +    } -    g_signal_emit_by_name(group, "added", iter); +    return result;  }  /******************************************************************************  *                                                                             * -*  Paramètres  : group = groupe de points d'arrêt à parcourir.                * -*                func  = fonction à appeler à chaque point trouvé.            * -*                data  = éventuelle donnée de l'utilisateur à joindre.        * +*  Paramètres  : bp = point d'arrêt à consulter.                              *  *                                                                             * -*  Description : Parcourt l'ensemble des points d'arrêt d'un groupe donné.    * +*  Description : Indique si un point d'arrêt a encore une utilité.            *  *                                                                             * -*  Retour      : -                                                            * +*  Retour      : true si le point peut être retiré, false sinon.              *  *                                                                             *  *  Remarques   : -                                                            *  *                                                                             *  ******************************************************************************/ -void g_break_group_for_each(GBreakGroup *group, GExtFunc func, gpointer data) +bool is_breakpoint_useless(const raw_breakpoint *bp)  { -    GBreakPoint *iter;                      /* Boucle de parcours          */ +    bool result;                            /* Conclusion à faire remonter */ -    /* Suppression d'un éventuel existant */ +    result = (bp->count == 0); -    bp_list_for_each(iter, group->points) -        func(group, iter, data); +    return result;  } diff --git a/src/debug/break.h b/src/debug/break.h index 5946ad2..77d959a 100644 --- a/src/debug/break.h +++ b/src/debug/break.h @@ -25,80 +25,58 @@  #define _DEBUG_BREAK_H -#include <glib-object.h> -#include <stdbool.h> +#include "misc.h" +#include "../arch/vmpa.h" -#include "../arch/archbase.h" -#include "../glibext/proto.h" +/* Origine des points d'arrêt en place */ +typedef enum _RawBpOrigin +{ +    RBO_INVALID  = (0 << 0),                /* Existance illégale          */ +    RBO_USER     = (1 << 0),                /* Point d'arrêt utilisateur   */ +    //RBO_COMPUTED = (1 << 1),                /* Arrêt sur un point spécial  */ +    RBO_INTERNAL = (1 << 2),                /* Restauration transparente   */ +    RBO_STEP     = (1 << 3),                /* Mise en place éphémère      */ +    RBO_COUNT -/* --------------------------- GESTION DES POINTS D'ARRET --------------------------- */ +} RawBpOrigin; -#define G_TYPE_BREAK_POINT               g_break_point_get_type() -#define G_BREAK_POINT(obj)               (G_TYPE_CHECK_INSTANCE_CAST((obj), g_break_point_get_type(), GBreakPoint)) -#define G_IS_BREAK_POINT(obj)            (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_break_point_get_type())) -#define G_BREAK_POINT_GET_IFACE(inst)    (G_TYPE_INSTANCE_GET_INTERFACE((inst), g_break_point_get_type(), GBreakPointIface)) -#define G_BREAK_POINT_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_BREAK_POINT, GBreakPointClass)) +/* Définition d'un point d'arrêt appliqué */ +typedef struct _raw_breakpoint raw_breakpoint; -/* Propriétés d'un point d'arrêt (instance) */ -typedef struct _GBreakPoint GBreakPoint; +/* Libère le coeur d'un point d'arrêt. */ +void fini_raw_breakpoint(raw_breakpoint *); -/* Propriétés d'un point d'arrêt (classe) */ -typedef struct _GBreakPointClass GBreakPointClass; +/* Indique l'adresse du point d'arrêt dans la mémoire ciblée. */ +virt_t get_raw_breakpoint_addr(const raw_breakpoint *); +/* Fournit l'adresse d'origine d'un point d'arrêt de pas à pas. */ +virt_t get_raw_breakpoint_prev_addr(const raw_breakpoint *); -/* Indique le type défini pour des propriétés d'un point d'arrêt. */ -GType g_break_point_get_type(void); +/* Effectue une comparaison entre adresse et point d'arrêt. */ +int compare_raw_breakpoint_with_addr(const virt_t *, const raw_breakpoint **); -/* Construit un nouveau point d'arrêt. */ -GBreakPoint *g_break_point_new(vmpa_t); +/* Effectue une comparaison entre deux points d'arrêt. */ +int compare_raw_breakpoints(const raw_breakpoint **, const raw_breakpoint **); -/* Fournit l'adresse associée à un point d'arrêt. */ -vmpa_t g_break_point_get_address(const GBreakPoint *); +/* Enregistre la source d'un point d'arrêt posé. */ +void set_raw_breakpoint_origin(raw_breakpoint *, RawBpOrigin, dbg_thread_id_t, virt_t); +/* Oublie la source d'un point d'arrêt posé. */ +void unset_raw_breakpoint_origin(raw_breakpoint *, RawBpOrigin, dbg_thread_id_t); +/* Indique si le point d'arrêt correspond à une source donnée. */ +bool has_raw_breakpoint_origin(const raw_breakpoint *, RawBpOrigin, dbg_thread_id_t); -/* ---------------------------- GROUPE DE POINTS D'ARRET ---------------------------- */ +/* Indique si le point d'arrêt correspond à une origine donnée. */ +bool has_raw_breakpoint_previous_address(const raw_breakpoint *, RawBpOrigin, dbg_thread_id_t, virt_t); - -#define G_TYPE_BREAK_GROUP               g_break_group_get_type() -#define G_BREAK_GROUP(obj)               (G_TYPE_CHECK_INSTANCE_CAST((obj), g_break_group_get_type(), GBreakGroup)) -#define G_IS_BREAK_GROUP(obj)            (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_break_group_get_type())) -#define G_BREAK_GROUP_GET_IFACE(inst)    (G_TYPE_INSTANCE_GET_INTERFACE((inst), g_break_group_get_type(), GBreakGroupIface)) -#define G_BREAK_GROUP_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_BREAK_GROUP, GBreakGroupClass)) - - -/* Propriétés d'un groupe de points d'arrêt (instance) */ -typedef struct _GBreakGroup GBreakGroup; - -/* Propriétés d'un groupe de points d'arrêt (classe) */ -typedef struct _GBreakGroupClass GBreakGroupClass; - - -/* Indique le type défini pour les groupes de points d'errêt. */ -GType g_break_group_get_type(void); - -/* Construit un nouveau groupe de points d'arrêt. */ -GBreakGroup *g_break_group_new(void); - -/* Fournit la désignation humaine associée à un groupe. */ -const char *g_break_group_get_name(const GBreakGroup *); - -/* Définit la désignation humaine à associer à un groupe. */ -void g_break_group_set_name(GBreakGroup *, const char *); - -/* Indique si une adresse donnée est gérée dans un groupe. */ -bool g_break_group_has_address(const GBreakGroup *, vmpa_t); - -/* Ajoute ou supprime un point d'arrêt dans un groupe. */ -void g_break_group_toggle_breakpoint(GBreakGroup *, vmpa_t); - -/* Parcourt l'ensemble des points d'arrêt d'un groupe donné. */ -void g_break_group_for_each(GBreakGroup *, GExtFunc, gpointer); +/* Indique si un point d'arrêt a encore une utilité. */ +bool is_breakpoint_useless(const raw_breakpoint *); diff --git a/src/debug/debugger-int.h b/src/debug/debugger-int.h index 06aa5d3..58153b8 100644 --- a/src/debug/debugger-int.h +++ b/src/debug/debugger-int.h @@ -28,7 +28,15 @@  #include "debugger.h" -#include <gtk/gtk.h> +#include "break.h" + + + +//#include <gtk/gtk.h>//////////////////////////////////////////// + + + + @@ -39,16 +47,71 @@ typedef bool (* attach_debugger_fc) (GBinaryDebugger *);  typedef bool (* basic_debugger_fc) (GBinaryDebugger *);  /* Reprend une procédure de débogage. */ -typedef bool (* resume_debugger_fc) (GBinaryDebugger *); +//typedef bool (* resume_debugger_fc) (GBinaryDebugger *);  /* Fournit les identifiants de tous les threads actifs. */ -typedef pid_t * (* dbg_list_all_threads_fc) (GBinaryDebugger *, char ***, size_t *); +//typedef pid_t * (* dbg_list_all_threads_fc) (GBinaryDebugger *, char ***, size_t *);  /* Fournit la liste des frames courantes d'un thread donné. */ -typedef dbg_frame_t * (* dbg_get_frames_stack_fc) (GBinaryDebugger *, pid_t, size_t *); +//typedef dbg_frame_t * (* dbg_get_frames_stack_fc) (GBinaryDebugger *, pid_t, size_t *);  /* Fournit la valeur des registres de l'architecture. */ -typedef register_value * (* get_register_values_fc) (GBinaryDebugger *, size_t *); +//typedef register_value * (* get_register_values_fc) (GBinaryDebugger *, size_t *); + + + + + + +/* Fournit les identifiants de tous les threads actifs. */ +typedef dbg_thread_desc * (* list_all_threads_fc) (GBinaryDebugger *, size_t *); + + + + +/* Lit une valeur quelconque à une adresse arbitraire. */ +typedef bool (* read_mem_any_fc) (GBinaryDebugger *, virt_t, size_t, ...); + +/* Ecrit une valeur quelconque à une adresse arbitraire. */ +typedef bool (* write_mem_any_fc) (GBinaryDebugger *, virt_t, size_t, ...); + +/* Liste l'ensemble des registres appartenant à un groupe. */ +typedef char ** (* get_reg_names_fc) (const GBinaryDebugger *, const char *, size_t *); + +/* Indique la taille associée à un registre donné. */ +typedef unsigned int (* get_reg_size_fc) (const GBinaryDebugger *, const char *); + +/* Lit une valeur quelconque à partir d'un registre. */ +typedef bool (* read_write_reg_any_fc) (GBinaryDebugger *, const char *, size_t, ...); + + + +/* Détermine le point d'exécution courant. */ +typedef bool (* get_current_pc_fc) (GBinaryDebugger *, virt_t *); + +/* Remonte la pile d'appels jusqu'au point courant. */ +typedef bool (* get_call_stack_fc) (GBinaryDebugger *, virt_t **, size_t *); + + + +/* Ajoute un point d'arrêt basique en mémoire. */ +typedef raw_breakpoint * (* enable_mem_bp_fc) (GBinaryDebugger *, virt_t); + +/* Retire un point d'arrêt basique en mémoire. */ +typedef bool (* disable_mem_bp_fc) (GBinaryDebugger *, raw_breakpoint *); + +/* Redémarre le processus de débogage lié à un serveur GDB. */ +typedef bool (* restart_debugger_fc) (GBinaryDebugger *); + +/* Remet en marche le débogueur courant. */ +typedef bool (* resume_debugger_fc) (GBinaryDebugger *); + + + + + + +  /* Définition des fonctionnalités d'un débogueur (instance) */ @@ -62,13 +125,13 @@ struct _GBinaryDebugger      basic_debugger_fc run;                  /* Démarre le débogueur        */      basic_debugger_fc pause;                /* Met en pause le débogueur   */ -    resume_debugger_fc resume;              /* Relance le débogueur        */      basic_debugger_fc kill;                 /* Tue le débogueur            */ -    dbg_list_all_threads_fc all_threads;    /* Liste des threads actifs    */ -    dbg_get_frames_stack_fc frames_stack;   /* Pile des frames courantes   */ -    get_register_values_fc get_reg_values;  /* Obtient les valeurs de reg. */ + +    raw_breakpoint **bpoints;               /* Points d'arrêt posés        */ +    size_t bp_count;                        /* Quantité de ces points posés*/ +    GRWLock bp_lock;                        /* Verrou d'accès à la liste   */  }; @@ -78,13 +141,58 @@ struct _GBinaryDebuggerClass  {      GObjectClass parent;                    /* A laisser en premier        */ +    list_all_threads_fc all_threads;        /* Liste des threads actifs    */ + +    read_mem_any_fc read_mem;               /* Lecture d'une valeur XX bits*/ +    read_mem_any_fc write_mem;              /* Ecriture d'une valeur X bits*/ +    get_reg_names_fc get_reg_names;         /* Liste des registres         */ +    get_reg_size_fc get_reg_size;           /* Taille d'un registre donné  */ +    read_write_reg_any_fc read_reg;         /* Lecture de registre XX bits */ +    read_write_reg_any_fc write_reg;        /* Ecriture de registre XX bits*/ + +    get_current_pc_fc get_current_pc;       /* Obtention du point d'exéc.  */ +    get_call_stack_fc get_call_stack;       /* Obtention de pile d'appels  */ + +    enable_mem_bp_fc enable_bp;             /* Mise en place d'un arrêt    */ +    disable_mem_bp_fc disable_bp;           /* Retrait d'un point d'arrêt  */ + +    restart_debugger_fc restart;            /* Redémarre le débogueur      */ +    resume_debugger_fc resume;              /* Relance le débogueur        */ +      /* Signaux */ -    void (* debugger_stopped) (GBinaryDebugger *, uint64_t, uint64_t); +    void (* signaled) (GBinaryDebugger *, int); +    void (* exited) (GBinaryDebugger *, int, pid_t); +    void (* terminated) (GBinaryDebugger *, int, pid_t); + +    void (* stopped) (GBinaryDebugger *, virt_t); + + + + +    /* Signaux */      void (* debugger_halted) (GBinaryDebugger *, int, vmpa_t, pid_t); + + + + +    void (* mem_bp_handled) (GBinaryDebugger *, bool, virt_t); +  }; + +/* ------------------------- MANIPULATION DE L'ETAT COURANT ------------------------- */ + + +/* Réagit à un arrêt du flot d'exécution. */ +void on_binary_debugger_stopped(GBinaryDebugger *, virt_t); + +/* Réagit à la fin de l'opération de débogage. */ +void on_binary_debugger_finished(GBinaryDebugger *, pid_t); + + +  #endif  /* _DEBUG_DEBUGGER_INT_H */ 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;  } diff --git a/src/debug/debugger.h b/src/debug/debugger.h index 6553b6d..1050e4e 100644 --- a/src/debug/debugger.h +++ b/src/debug/debugger.h @@ -27,51 +27,23 @@  #include <glib-object.h>  #include <stdint.h> -#include <sys/types.h> +#include "misc.h"  #include "../analysis/binary.h"  #include "../arch/archbase.h" -/* Liste de tous les débogueurs */ -typedef enum _DebuggerType -{ -    DGT_JDWP,                               /* Utilisation du JDWP         */ -    DGT_REMOTE_GDB,                         /* Utilisation de GDB          */ -    DGT_PTRACE,                             /* Utilisation de ptrace()     */ - -    DGT_COUNT2/* FIXME */ - -} DebuggerType; - - -/* Définition d'une frame */ -typedef struct _dbg_frame_t -{ - -    vmpa_t addr;                            /* Position dans le code       */ - -} dbg_frame_t; - - - -/* Transmission des valeurs des registres */ -typedef struct _register_value -{ -    const char *name;                       /* Nom à ne pas libérer        */ -    uint64_t value;                         /* Valeur (taille maximale)    */ - -} register_value; +/* ---------------------------- TRONC COMMUN DE DEBOGAGE ---------------------------- */  #define G_TYPE_BINARY_DEBUGGER              g_binary_debugger_get_type()  #define G_BINARY_DEBUGGER(obj)              (G_TYPE_CHECK_INSTANCE_CAST((obj), g_binary_debugger_get_type(), GBinaryDebugger))  #define G_IS_BINARY_DEBUGGER(obj)           (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_binary_debugger_get_type())) -#define G_BINARY_DEBUGGER_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_BINARY_DEBUGGER, GGBinaryDebuggerClass)) +#define G_BINARY_DEBUGGER_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_BINARY_DEBUGGER, GBinaryDebuggerClass))  #define G_IS_BINARY_DEBUGGER_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_BINARY_DEBUGGER)) -#define G_BINARY_DEBUGGER_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_BINARY_DEBUGGER, GGBinaryDebuggerClass)) +#define G_BINARY_DEBUGGER_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_BINARY_DEBUGGER, GBinaryDebuggerClass))  /* Définition des fonctionnalités d'un débogueur (instance) */ @@ -84,9 +56,6 @@ typedef struct _GBinaryDebuggerClass GBinaryDebuggerClass;  /* Indique le type définit par la GLib pour le débogueur ptrace(). */  GType g_binary_debugger_get_type(void); -/* Crée un nouveau débogueur. */ -GBinaryDebugger *g_new_binary_debugger(DebuggerType, GLoadedBinary *); -  /* Démarre une procédure de débogage. */  bool g_binary_debugger_attach(GBinaryDebugger *); @@ -94,19 +63,165 @@ bool g_binary_debugger_attach(GBinaryDebugger *);  void g_binary_debugger_run(GBinaryDebugger *);  /* Reprend une procédure de débogage. */ -void g_binary_debugger_resume(GBinaryDebugger *); +//void g_binary_debugger_resume(GBinaryDebugger *);  /* Tue une procédure de débogage. */  void g_binary_debugger_kill(GBinaryDebugger *);  /* Fournit les identifiants de tous les threads actifs. */ -pid_t *g_binary_debugger_list_all_threads(GBinaryDebugger *, char ***, size_t *); +//pid_t *g_binary_debugger_list_all_threads(GBinaryDebugger *, char ***, size_t *);  /* Fournit la liste des frames courantes d'un thread donné. */ -dbg_frame_t *g_binary_debugger_get_frames_stack(GBinaryDebugger *, pid_t, size_t *); +//dbg_frame_t *g_binary_debugger_get_frames_stack(GBinaryDebugger *, pid_t, size_t *);  /* Fournit la valeur des registres de l'architecture. */ -register_value *g_binary_debugger_get_registers(GBinaryDebugger *, size_t *); +//register_value *g_binary_debugger_get_registers(GBinaryDebugger *, size_t *); + + + + + + + + + + + + + + +/* ---------------------------------------------------------------------------------- */ +/*                     MANIPULATION DES DIFFERENTS THREADS ACTIFS                     */ +/* ---------------------------------------------------------------------------------- */ + + +/* Description humaine à représenter */ +typedef struct _dbg_thread_desc +{ +    dbg_thread_id_t id;                     /* Identifiant interne         */ +    char *name;                             /* Dénomination humaine        */ + +} dbg_thread_desc; + + +/* Libère la mémoire d'une liste de threads actifs. */ +void delete_dbg_thread_desc(dbg_thread_desc *, size_t); + +/* Fournit les identifiants de tous les threads actifs. */ +dbg_thread_desc *g_binary_debugger_list_all_threads(GBinaryDebugger *, size_t *); + + + + + + + + + +/* --------------------------- ENTREES / SORTIES BASIQUES --------------------------- */ + + +/* Lit une valeur de 8 bits à une adresse arbitraire. */ +bool g_binary_debugger_read_memory_u8(GBinaryDebugger *, virt_t, uint8_t *); + +/* Lit une valeur de 16 bits à une adresse arbitraire. */ +bool g_binary_debugger_read_memory_u16(GBinaryDebugger *, virt_t, uint16_t *); + +/* Lit une valeur de 32 bits à une adresse arbitraire. */ +bool g_binary_debugger_read_memory_u32(GBinaryDebugger *, virt_t, uint32_t *); + +/* Lit une valeur de 64 bits à une adresse arbitraire. */ +bool g_binary_debugger_read_memory_u64(GBinaryDebugger *, virt_t, uint64_t *); + +/* Lit une valeur de taille quelconque à une adresse arbitraire. */ +bool g_binary_debugger_read_memory_data(GBinaryDebugger *, virt_t, uint8_t *, size_t); + +/* Ecrit une valeur de 8 bits à une adresse arbitraire. */ +bool g_binary_debugger_write_memory_u8(GBinaryDebugger *, virt_t, const uint8_t *); + +/* Ecrit une valeur de 16 bits à une adresse arbitraire. */ +bool g_binary_debugger_write_memory_u16(GBinaryDebugger *, virt_t, const uint16_t *); + +/* Ecrit une valeur de 32 bits à une adresse arbitraire. */ +bool g_binary_debugger_write_memory_u32(GBinaryDebugger *, virt_t, const uint32_t *); + +/* Ecrit une valeur de 64 bits à une adresse arbitraire. */ +bool g_binary_debugger_write_memory_u64(GBinaryDebugger *, virt_t, const uint64_t *); + +/* Ecrit une valeur de taille quelconque à une adresse donnée. */ +bool g_binary_debugger_write_memory_data(GBinaryDebugger *, virt_t, const uint8_t *, size_t); + +/* Liste l'ensemble des registres appartenant à un groupe. */ +char **g_binary_debugger_get_register_names(const GBinaryDebugger *, const char *, size_t *); + +/* Indique la taille associée à un registre donné. */ +unsigned int g_binary_debugger_get_register_size(const GBinaryDebugger *, const char *); + +/* Lit une valeur de 8 bits à partir d'un registre. */ +bool g_binary_debugger_read_register_u8(GBinaryDebugger *, const char *, uint8_t *); + +/* Lit une valeur de 16 bits à partir d'un registre. */ +bool g_binary_debugger_read_register_u16(GBinaryDebugger *, const char *, uint16_t *); + +/* Lit une valeur de 32 bits à partir d'un registre. */ +bool g_binary_debugger_read_register_u32(GBinaryDebugger *, const char *, uint32_t *); + +/* Lit une valeur de 64 bits à partir d'un registre. */ +bool g_binary_debugger_read_register_u64(GBinaryDebugger *, const char *, uint64_t *); + +/* Ecrit une valeur de 8 bits dans un registre. */ +bool g_binary_debugger_write_register_u8(GBinaryDebugger *, const char *, const uint8_t *); + +/* Ecrit une valeur de 16 bits dans un registre. */ +bool g_binary_debugger_write_register_u16(GBinaryDebugger *, const char *, const uint16_t *); + +/* Ecrit une valeur de 32 bits dans un registre. */ +bool g_binary_debugger_write_register_u32(GBinaryDebugger *, const char *, const uint32_t *); + +/* Ecrit une valeur de 64 bits dans un registre. */ +bool g_binary_debugger_write_register_u64(GBinaryDebugger *, const char *, const uint64_t *); + + + +/* ------------------------- MANIPULATION DE L'ETAT COURANT ------------------------- */ + + +/* Détermine le point d'exécution courant. */ +bool g_binary_debugger_get_current_pc(GBinaryDebugger *, virt_t *); + +/* Détermine l'adresse du premier retour d'appel. */ +bool g_binary_debugger_get_return_pc(GBinaryDebugger *, virt_t *); + +/* Détermine les prochaines probables instructions exécutées. */ +virt_t *g_binary_debugger_get_next_pcs(GBinaryDebugger *, virt_t, bool, size_t *); + +/* Remonte la pile d'appels jusqu'au point courant. */ +bool g_binary_debugger_get_call_stack(GBinaryDebugger *, virt_t **, size_t *); + + + +/* --------------------------- GESTION DES POINTS D'ARRET --------------------------- */ + + +/* Ajoute un point d'arrêt basique en mémoire. */ +bool g_binary_debugger_add_memory_breakpoint(GBinaryDebugger *, virt_t); + +/* Retire un point d'arrêt basique en mémoire. */ +bool g_binary_debugger_delete_memory_breakpoint(GBinaryDebugger *, virt_t); + + + +/* -------------------------- CONTROLE DU FLOT D'EXECUTION -------------------------- */ + + +/* Redémarre le processus de débogage. */ +bool g_binary_debugger_restart(GBinaryDebugger *); + +/* Remet en marche le débogueur courant. */ +bool g_binary_debugger_resume(GBinaryDebugger *); + +/* Relance l'exécution pour une seule instruction. */ +bool g_binary_debugger_stepi(GBinaryDebugger *, bool); diff --git a/src/debug/gdbrsp/Makefile.am b/src/debug/gdbrsp/Makefile.am new file mode 100644 index 0000000..fd46f27 --- /dev/null +++ b/src/debug/gdbrsp/Makefile.am @@ -0,0 +1,24 @@ + +noinst_LTLIBRARIES = libdebuggdbrsp.la + +libdebuggdbrsp_la_SOURCES =				\ +	aops.h								\ +	gdb-int.h							\ +	gdb.h gdb.c							\ +	helpers.h helpers.c					\ +	helpers_arm.h helpers_arm.c			\ +	helpers_arm64.h helpers_arm64.c		\ +	packet.h packet.c					\ +	stream-int.h						\ +	stream.h stream.c					\ +	support.h support.c					\ +	target.h target.c					\ +	tcp.h tcp.c							\ +	utils.h utils.c + +libdebuggdbrsp_la_CFLAGS = $(AM_CFLAGS) + + +AM_CPPFLAGS = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS) + +AM_CFLAGS = $(DEBUG_CFLAGS) $(WARNING_FLAGS) $(COMPLIANCE_FLAGS) diff --git a/src/debug/gdbrsp/aops.h b/src/debug/gdbrsp/aops.h new file mode 100644 index 0000000..02d4982 --- /dev/null +++ b/src/debug/gdbrsp/aops.h @@ -0,0 +1,57 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * aops.h - prototypes pour les compléments utiles à GDB pour l'architecture ARM + * + * Copyright (C) 2017 Cyrille Bagard + * + *  This file is part of Chrysalide. + * + *  Chrysalide is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 3 of the License, or + *  (at your option) any later version. + * + *  Chrysalide is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with Foobar.  If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _DEBUG_GDBRSP_AOPS_H +#define _DEBUG_GDBRSP_AOPS_H + + +#include "gdb.h" + + + +/* Détermine le point d'exécution courant. */ +typedef bool (* get_pc_fc) (GGdbDebugger *, virt_t *); + +/* Remonte la pile d'appels jusqu'au point courant. */ +typedef bool (* compute_call_stack_fc) (const GGdbDebugger *, virt_t **, size_t *); + +/* Complète la commande manipulant des points d'arrêt. */ +typedef const char * (* get_bp_kind_fc) (const GGdbDebugger *, virt_t); + +/* Construit une instruction provoquant un arrêt d'exécution. */ +typedef const uint8_t * (* get_bp_data_fc) (const GGdbDebugger *, virt_t, size_t *); + + +/* Procédures spécifiques pour une architecture */ +typedef struct _gdb_arch_ops +{ +    get_pc_fc get_pc;                       /* Obtention du point d'exéc.  */ +    compute_call_stack_fc compute_cstack;   /* Calcule la pile d'appels    */ +    get_bp_kind_fc get_bp_kind;             /* Fournit le type d'un point  */ +    get_bp_data_fc get_bp_data;             /* Code d'un point d'arrêt     */ + +} gdb_arch_ops; + + + +#endif  /* _DEBUG_GDBRSP_AOPS_H */ diff --git a/src/debug/gdbrsp/gdb-int.h b/src/debug/gdbrsp/gdb-int.h new file mode 100644 index 0000000..5055fbf --- /dev/null +++ b/src/debug/gdbrsp/gdb-int.h @@ -0,0 +1,116 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * gdb.h - prototypes pour le débogage à l'aide de gdb. + * + * Copyright (C) 2009-2012 Cyrille Bagard + * + *  This file is part of Chrysalide. + * + *  Chrysalide is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 3 of the License, or + *  (at your option) any later version. + * + *  Chrysalide is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with Foobar.  If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _DEBUG_GDBRSP_GDB_INT_H +#define _DEBUG_GDBRSP_GDB_INT_H + + +#include "aops.h" +#include "gdb.h" +#include "stream.h" +#include "support.h" +#include "target.h" +#include "../break-int.h" +#include "../debugger-int.h" + + + + + + + + + + +/* Définition d'un point d'arrêt appliqué */ +typedef struct _gdb_breakpoint +{ +    raw_breakpoint raw;                     /* A laisser en premier        */ + +    bool is_z;                              /* Usage de commande dédiée ?  */ + +    union +    { +        const char *kind;                   /* Précision de taille         */ + +        struct +        { +            uint8_t memory[16];             /* Données d'origine remplacées*/ +            size_t len;                     /* Quantité de ces données     */ +        }; + +    }; + +} gdb_breakpoint; + + + + + + + + + + + + + + +/* Débogueur utilisant un serveur GDB (instance) */ +struct _GGdbDebugger +{ +    GBinaryDebugger parent;                 /* A laisser en premier        */ + +    SourceEndian endian;                    /* Boutisme du format          */ +    MemoryDataSize msize;                   /* Taille des adresses         */ + +    const gdb_arch_ops *ops;                /* Opérations spécifiques      */ + +    GGdbStream *stream;                     /* Flux de communication       */ +    GGdbSupport *support;                   /* Configuration à adopter     */ +    GGdbTarget *target;                     /* Architecture ciblée par GDB */ + +}; + + +/* Débogueur utilisant un serveur GDB (classe) */ +struct _GGdbDebuggerClass +{ +    GBinaryDebuggerClass parent;            /* A laisser en premier        */ + +}; + + + +/* ------------------------ ACCUEIL D'EVENEMENTS ASYNCHRONES ------------------------ */ + + +/* Réagit à la réception d'un signal par le programme étudié. */ +void g_gdb_debugger_receive_signal_reply(GGdbDebugger *, int); + +/* Réagit à la sortie d'exécution d'un programme étudié. */ +void g_gdb_debugger_receive_exit_reply(GGdbDebugger *, int, pid_t); + + + +#endif  /* _DEBUG_GDBRSP_GDB_INT_H */ diff --git a/src/debug/gdbrsp/gdb.c b/src/debug/gdbrsp/gdb.c new file mode 100644 index 0000000..37a7a73 --- /dev/null +++ b/src/debug/gdbrsp/gdb.c @@ -0,0 +1,1524 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * gdb.c - débogage à l'aide de gdb. + * + * Copyright (C) 2009-2017 Cyrille Bagard + * + *  This file is part of Chrysalide. + * + *  Chrysalide is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 3 of the License, or + *  (at your option) any later version. + * + *  Chrysalide is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with Foobar.  If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "gdb.h" + + + +#include <assert.h> +#include <malloc.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> + + +#include "gdb-int.h" +#include "helpers.h" +#include "helpers_arm.h" +#include "helpers_arm64.h" +#include "tcp.h" +#include "utils.h" +#include "../../common/cpp.h" +#include "../../format/format.h" + + + + + + + + +/* Initialise la classe du débogueur utilisant gdb. */ +static void g_gdb_debugger_class_init(GGdbDebuggerClass *); + +/* Procède à l'initialisation du débogueur utilisant gdb. */ +static void g_gdb_debugger_init(GGdbDebugger *); + +/* Supprime toutes les références externes. */ +static void g_gdb_debugger_dispose(GGdbDebugger *); + +/* Procède à la libération totale de la mémoire. */ +static void g_gdb_debugger_finalize(GGdbDebugger *); + + +/* Met en marche le débogueur utilisant un serveur GDB. */ +static bool g_gdb_debugger_run(GGdbDebugger *); + +/* Remet en marche le débogueur utilisant un serveur GDB. */ +//static bool g_gdb_debugger_resume(GGdbDebugger *); + +/* Tue le débogueur utilisant un serveur GDB. */ +static bool g_gdb_debugger_kill(GGdbDebugger *); + + + + + + +/* --------------------------- ENTREES / SORTIES BASIQUES --------------------------- */ + + +/* Lit une valeur quelconque à une adresse arbitraire.  */ +static bool g_gdb_debugger_read_memory(GGdbDebugger *, virt_t, size_t, ...); + +/* Ecrit une valeur quelconque à une adresse arbitraire.  */ +static bool g_gdb_debugger_write_memory(GGdbDebugger *, virt_t, size_t, ...); + +/* Liste l'ensemble des registres appartenant à un groupe. */ +static char **g_gdb_debugger_get_register_names(const GGdbDebugger *, const char *, size_t *); + +/* Indique la taille associée à un registre donné. */ +static unsigned int g_gdb_debugger_get_register_size(const GGdbDebugger *, const char *); + +/* Effectue la lecture d'un registre donné. */ +static bool g_gdb_debugger_read_register(GGdbDebugger *, const char *, size_t, ...); + +/* Effectue l'écriture d'un registre donné. */ +static bool g_gdb_debugger_write_register(GGdbDebugger *, const char *, size_t, ...); + + + +/* ------------------------- MANIPULATION DE L'ETAT COURANT ------------------------- */ + + +/* Détermine le point d'exécution courant. */ +static bool g_gdb_debugger_get_current_pc(GGdbDebugger *, virt_t *); + +/* Remonte la pile d'appels jusqu'au point courant. */ +static bool g_gdb_debugger_compute_call_stack(GGdbDebugger *, virt_t **, size_t *); + + + +/* --------------------------- GESTION DES POINTS D'ARRET --------------------------- */ + + +/* Ajoute un point d'arrêt basique en mémoire. */ +static gdb_breakpoint *g_gdb_debugger_enable_memory_breakpoint(GGdbDebugger *, virt_t); + +/* Retire un point d'arrêt basique en mémoire. */ +static bool g_gdb_debugger_disable_memory_breakpoint(GGdbDebugger *, gdb_breakpoint *); + + + +/* -------------------------- CONTROLE DU FLOT D'EXECUTION -------------------------- */ + + +/* Redémarre le processus de débogage lié à un serveur GDB. */ +static bool g_gdb_debugger_restart(GGdbDebugger *); + +/* Remet en marche le débogueur utilisant un serveur GDB. */ +static bool g_gdb_debugger_resume(GGdbDebugger *); + + + + + + + + + + + + +/* Détermine l'identifiant du thread principal courant. */ +static char *g_gdb_debugger_get_active_thread(GGdbDebugger *); + + + + + + +/* ------------------------ ACCUEIL D'EVENEMENTS ASYNCHRONES ------------------------ */ + + + + + + + + +/* Indique le type défini par la GLib pour le débogueur gdb. */ +G_DEFINE_TYPE(GGdbDebugger, g_gdb_debugger, G_TYPE_BINARY_DEBUGGER); + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : klass = classe de débogueur à initialiser.                   * +*                                                                             * +*  Description : Initialise la classe du débogueur utilisant gdb.             * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void g_gdb_debugger_class_init(GGdbDebuggerClass *klass) +{ +    GObjectClass *object;                   /* Autre version de la classe  */ +    GBinaryDebuggerClass *parent;           /* Version en classe parente   */ + +    object = G_OBJECT_CLASS(klass); + +    object->dispose = (GObjectFinalizeFunc/* ! */)g_gdb_debugger_dispose; +    object->finalize = (GObjectFinalizeFunc)g_gdb_debugger_finalize; + +    parent = G_BINARY_DEBUGGER_CLASS(klass); + +    parent->read_mem = (read_mem_any_fc)g_gdb_debugger_read_memory; +    parent->write_mem = (write_mem_any_fc)g_gdb_debugger_write_memory; +    parent->get_reg_names = (get_reg_names_fc)g_gdb_debugger_get_register_names; +    parent->get_reg_size = (get_reg_size_fc)g_gdb_debugger_get_register_size; +    parent->read_reg = (read_write_reg_any_fc)g_gdb_debugger_read_register; +    parent->write_reg = (read_write_reg_any_fc)g_gdb_debugger_write_register; + +    parent->get_current_pc = (get_current_pc_fc)g_gdb_debugger_get_current_pc; +    parent->get_call_stack = (get_call_stack_fc)g_gdb_debugger_compute_call_stack; + +    parent->enable_bp = (enable_mem_bp_fc)g_gdb_debugger_enable_memory_breakpoint; +    parent->disable_bp = (disable_mem_bp_fc)g_gdb_debugger_disable_memory_breakpoint; + +    parent->restart = (restart_debugger_fc)g_gdb_debugger_restart; +    parent->resume = (resume_debugger_fc)g_gdb_debugger_resume; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = instance de débogueur à préparer.                 * +*                                                                             * +*  Description : Procède à l'initialisation du débogueur utilisant gdb.       * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void g_gdb_debugger_init(GGdbDebugger *debugger) +{ +    GBinaryDebugger *parent;                /* Instance parente            */ + +    parent = G_BINARY_DEBUGGER(debugger); + +    parent->run = (basic_debugger_fc)g_gdb_debugger_run; +    //parent->resume = (resume_debugger_fc)g_gdb_debugger_resume; +    parent->kill = (basic_debugger_fc)g_gdb_debugger_kill; + +    //parent->get_reg_values = (get_register_values_fc)get_register_values_using_gdb_debugger; + +    //debugger->cond = g_cond_new(); +    //debugger->mutex = g_mutex_new(); + + +    // FIXME +    //debugger->compute_cstack = compute_call_stack_for_arm64; +    //debugger->fill_mem_bp = fill_memory_breakpoint_cmd_for_arm64; + + + + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = instance d'objet GLib à traiter.                  * +*                                                                             * +*  Description : Supprime toutes les références externes.                     * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void g_gdb_debugger_dispose(GGdbDebugger *debugger) +{ +    if (debugger->stream != NULL) +        g_object_unref(G_OBJECT(debugger->stream)); + +    if (debugger->support != NULL) +        g_object_unref(G_OBJECT(debugger->support)); + +    if (debugger->target != NULL) +        g_object_unref(G_OBJECT(debugger->target)); + +    G_OBJECT_CLASS(g_gdb_debugger_parent_class)->dispose(G_OBJECT(debugger)); + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = instance d'objet GLib à traiter.                  * +*                                                                             * +*  Description : Procède à la libération totale de la mémoire.                * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void g_gdb_debugger_finalize(GGdbDebugger *debugger) +{ +    G_OBJECT_CLASS(g_gdb_debugger_parent_class)->finalize(G_OBJECT(debugger)); + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : binary = binaire représenter à déboguer.                     * +*                server = nom ou adresse du serveur à contacter.              * +*                port   = port de connexion.                                  * +*                                                                             * +*  Description : Crée un débogueur utilisant un serveur GDB distant.          * +*                                                                             * +*  Retour      : Instance de débogueur mise en place ou NULL.                 * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +GBinaryDebugger *g_gdb_debugger_new(GLoadedBinary *binary, const char *server, unsigned short port) +{ +    GGdbDebugger *result;                   /* Débogueur à retourner       */ +    GExeFormat *format;                     /* Format du binaire chargé    */ +    const char *arch;                       /* Architecture d'exécution    */ +    GArchProcessor *proc;                   /* Processeur lié au binaire   */ +    char service[sizeof(XSTR(UINT16_MAX)) + 1]; /* Conversion requise      */ + +    result = g_object_new(G_TYPE_GDB_DEBUGGER, NULL); + +    G_BINARY_DEBUGGER(result)->binary = binary; +    g_object_ref(G_OBJECT(binary)); + +    /* Propriétés de la cible */ + +    format = g_loaded_binary_get_format(binary); + +    result->endian = g_binary_format_get_endianness(G_BIN_FORMAT(format)); + +    arch = g_exe_format_get_target_machine(format); + +    if (strcmp(arch, "armv7") == 0) +        result->ops = get_arm_operations(); +    else +        result->ops = NULL; + +    g_object_unref(G_OBJECT(format)); + +    if (result->ops == NULL) +        goto ggdn_error; + +    proc = g_loaded_binary_get_processor(binary); + +    result->msize = g_arch_processor_get_memory_size(proc); + +    g_object_unref(G_OBJECT(proc)); + +    /* Mise en place des modules auxialiaires */ + +    snprintf(service, sizeof(service), "%hu", port); + +    result->stream = g_gdb_tcp_client_new(server, service, result); +    if (result->stream == NULL) goto ggdn_error; + +    result->support = g_gdb_support_new(result->stream); + +    result->target = g_gdb_target_new(result->stream); +    if (result->target == NULL) goto ggdn_error; + +    return G_BINARY_DEBUGGER(result); + + ggdn_error: + +    g_object_unref(G_OBJECT(result)); + +    return NULL; + +} + + + + + + + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = débogueur à lancer.                               * +*                                                                             * +*  Description : Met en marche le débogueur utilisant un serveur GDB.         * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_debugger_run(GGdbDebugger *debugger) +{ + + + +    GGdbPacket *packet; + +    bool test; + +    const char *data; +    size_t len; + + +    int sig; +    vmpa_t addr; +    pid_t thread; + + +    debugger->stream = g_gdb_tcp_client_new("127.0.0.1", "6666", NULL); +    if (debugger->stream == NULL) return false; + + +    printf("Connection done !\n"); + + + +    packet = g_gdb_stream_get_free_packet(debugger->stream); + +    g_gdb_packet_start_new_command(packet); +    g_gdb_packet_append(packet, "?"); + + +    test = g_gdb_stream_send_packet(debugger->stream, packet); + + + +    printf(" >> Paquet '%s' bien envoyé ? %s\n", "?", test ? "oui" : "non"); + + + +    g_gdb_stream_mark_packet_as_free(debugger->stream, packet); + +    packet = g_gdb_stream_recv_packet(debugger->stream); + +    g_gdb_packet_get_data(packet, &data, &len, NULL); + +    printf(" << Réception de '%s'\n", data); + + + + + +    get_stop_reply_sig_info(packet, &sig, &addr, &thread, SRE_LITTLE); + +    g_signal_emit_by_name(debugger, "halted", sig, addr, thread); + + + +    return true; + +} + + + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = débogueur à relancer.                             * +*                                                                             * +*  Description : Tue le débogueur utilisant un serveur GDB.                   * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_debugger_kill(GGdbDebugger *debugger) +{ + + +#if 0 +    int ret;                                /* Bilan de l'appel système    */ + +    ret = kill(debugger->child, SIGKILL); +    if (ret != 0) perror("kill"); + +    debugger->child = 0; + +    g_mutex_lock(debugger->mutex); +    debugger->run_again = TRUE; +    g_cond_signal(debugger->cond); +    g_mutex_unlock(debugger->mutex); +#endif +    return true; + +} + + + + + + + + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = débogueur à consulter.                            * +*                                                                             * +*  Description : Détermine l'identifiant du thread principal courant.         * +*                                                                             * +*  Retour      : Identifiant du thread actif principal ou NULL en cas d'échec.* +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static char *g_gdb_debugger_get_active_thread(GGdbDebugger *debugger) +{ +    char *result;                           /* Identifiant à renvoyer      */ +    GGdbPacket *packet;                     /* Paquet de communication     */ +    bool status;                            /* Bilan d'une communication   */ +    const char *data;                       /* Données reçues à analyser   */ +    const char *start;                      /* Début d'identification      */ +    const char *end;                        /* Fin d'identification        */ + +    result = NULL; + +    /* Envoi de la requête */ + +    packet = g_gdb_stream_get_free_packet(debugger->stream); + +    g_gdb_packet_start_new_command(packet); +    g_gdb_packet_append(packet, "?"); + +    status = g_gdb_stream_send_packet(debugger->stream, packet); + +    if (!status) +        goto ggdgat_exit; + +    /* Réception de la réponse */ + +    packet = g_gdb_stream_recv_packet(debugger->stream); + +    g_gdb_packet_get_data(packet, &data, NULL, NULL); + +    start = strstr(data, "thread:"); +    if (start == NULL) goto ggdgat_exit; + +    start += sizeof("thread:") - 1 /* '\0' */; + +    end = strstr(start, ";"); +    if (end == NULL) goto ggdgat_exit; + +    result = strndup(start, end - start); + + ggdgat_exit: + +    g_gdb_stream_mark_packet_as_free(debugger->stream, packet); + +    return result; + +} + + + + + + + + + + + + + + + + + + + + +/* ---------------------------------------------------------------------------------- */ +/*                             ENTREES / SORTIES BASIQUES                             */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = débogueur à consulter.                            * +*                addr     = emplacement en mémoire à venir consulter.         * +*                size     = taille des données mises en jeu.                  * +*                ...      = emplacement de la valeur lue à conserver. [OUT]   * +*                                                                             * +*  Description : Lit une valeur quelconque à une adresse arbitraire.          * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_debugger_read_memory(GGdbDebugger *debugger, virt_t addr, size_t size, ...) +{ +    bool result;                            /* Bilan d'opération à renvoyer*/ +    char cmd[1 + VMPA_MAX_LEN + 3];         /* Commande à émettre          */ +    GGdbPacket *packet;                     /* Paquet de communication     */ +    const char *data;                       /* Données reçues à analyser   */ +    size_t len;                             /* Quantité de données reçues  */ +    va_list ap;                             /* Liste variable d'arguments  */ +    uint8_t *val8;                          /* Valeur sur 8 bits           */ +    uint16_t *val16;                        /* Valeur sur 16 bits          */ +    uint16_t conv16;                        /* Valeur adaptée sur 16 bits  */ +    uint32_t *val32;                        /* Valeur sur 32 bits          */ +    uint32_t conv32;                        /* Valeur adaptée sur 32 bits  */ +    uint64_t *val64;                        /* Valeur sur 64 bits          */ +    uint64_t conv64;                        /* Valeur adaptée sur 64 bits  */ + +    /* Envoi de la requête */ + +    cmd[0] = 'm'; + +    result = translate_virt_to_hex(debugger, addr, &cmd[1]); + +    switch (size) +    { +        case 8: +            strcat(cmd, ",1"); +            break; + +        case 16: +            strcat(cmd, ",2"); +            break; + +        case 32: +            strcat(cmd, ",4"); +            break; + +        case 64: +            strcat(cmd, ",8"); +            break; + +        default: +            assert(false); +            result = false; +            goto ggdrm_exit; +            break; + +    } + +    packet = g_gdb_stream_get_free_packet(debugger->stream); + +    g_gdb_packet_start_new_command(packet); +    g_gdb_packet_append(packet, cmd); + +    result = g_gdb_stream_send_packet(debugger->stream, packet); + +    g_gdb_stream_mark_packet_as_free(debugger->stream, packet); + +    if (!result) +        goto ggdrm_exit; + +    /* Réception de la réponse */ + +    packet = g_gdb_stream_recv_packet(debugger->stream); + +    g_gdb_packet_get_data(packet, &data, &len, NULL); + +    if (is_error_code(data, len)) +    { +        result = false; +        goto ggdrm_error; +    } + +    va_start(ap, size); + +    switch (size) +    { +        case 8: +            val8 = va_arg(ap, uint8_t *); +            result = hex_to_u8(data, val8); +            break; + +        case 16: +            val16 = va_arg(ap, uint16_t *); +            result = hex_to_u16(data, &conv16); +            *val16 = from_u16(&conv16, debugger->endian); +            break; + +        case 32: +            val32 = va_arg(ap, uint32_t *); +            result = hex_to_u32(data, &conv32); +            *val32 = from_u32(&conv32, debugger->endian); +            break; + +        case 64: +            val64 = va_arg(ap, uint64_t *); +            result = hex_to_u64(data, &conv64); +            *val64 = from_u64(&conv64, debugger->endian); +            break; + +        default: +            assert(false); +            result = false; +            break; + +    } + +    va_end(ap); + + ggdrm_error: + +    g_gdb_stream_mark_packet_as_free(debugger->stream, packet); + + ggdrm_exit: + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = débogueur à manipuler.                            * +*                addr     = emplacement en mémoire à venir consulter.         * +*                size     = taille des données mises en jeu.                  * +*                ...      = emplacement de la valeur lue à conserver. [OUT]   * +*                                                                             * +*  Description : Lit une valeur quelconque à une adresse arbitraire.          * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_debugger_write_memory(GGdbDebugger *debugger, virt_t addr, size_t size, ...) +{ +    bool result;                            /* Bilan d'opération à renvoyer*/ +    char cmd[1 + 3 * VMPA_MAX_LEN + 3];     /* Commande à émettre          */ +    va_list ap;                             /* Liste variable d'arguments  */ +    const uint8_t *val8;                    /* Valeur sur 8 bits           */ +    const uint16_t *val16;                  /* Valeur sur 16 bits          */ +    uint16_t conv16;                        /* Valeur adaptée sur 16 bits  */ +    const uint32_t *val32;                  /* Valeur sur 32 bits          */ +    uint32_t conv32;                        /* Valeur adaptée sur 32 bits  */ +    const uint64_t *val64;                  /* Valeur sur 64 bits          */ +    uint64_t conv64;                        /* Valeur adaptée sur 64 bits  */ +    char hexval[17];                        /* Valeur sous forme hexa      */ +    GGdbPacket *packet;                     /* Paquet de communication     */ +    const char *data;                       /* Données reçues à analyser   */ +    size_t len;                             /* Quantité de données reçues  */ + +    /* Envoi de la requête */ + +    cmd[0] = 'M'; + +    result = translate_virt_to_hex(debugger, addr, &cmd[1]); + +    va_start(ap, size); + +    switch (size) +    { +        case 8: +            val8 = va_arg(ap, uint8_t *); +            result = u8_to_hex(val8, hexval); + +            strcat(cmd, ",1:"); +            strcat(cmd, hexval); +            break; + +        case 16: +            val16 = va_arg(ap, uint16_t *); +            conv16 = to_u16(val16, debugger->endian); +            result = u16_to_hex(&conv16, hexval); + +            strcat(cmd, ",2:"); +            strcat(cmd, hexval); +            break; + +        case 32: +            val32 = va_arg(ap, uint32_t *); +            conv32 = to_u32(val32, debugger->endian); +            result = u32_to_hex(&conv32, hexval); + +            strcat(cmd, ",4:"); +            strcat(cmd, hexval); +            break; + +        case 64: +            val64 = va_arg(ap, uint64_t *); +            conv64 = to_u64(val64, debugger->endian); +            result = u16_to_hex(&conv64, hexval); + +            strcat(cmd, ",8:"); +            strcat(cmd, hexval); +            break; + +        default: +            assert(false); +            result = false; +            break; + +    } + +    if (!result) +        goto ggdwm_exit; + +    packet = g_gdb_stream_get_free_packet(debugger->stream); + +    g_gdb_packet_start_new_command(packet); +    g_gdb_packet_append(packet, cmd); + +    result = g_gdb_stream_send_packet(debugger->stream, packet); + +    g_gdb_stream_mark_packet_as_free(debugger->stream, packet); + +    if (!result) +        goto ggdwm_exit; + +    /* Réception de la réponse */ + +    packet = g_gdb_stream_recv_packet(debugger->stream); + +    g_gdb_packet_get_data(packet, &data, &len, NULL); + +    if (len == 3 && data[0] == 'E') +    { +        result = false; +        goto ggdrm_error; +    } + + ggdrm_error: + +    g_gdb_stream_mark_packet_as_free(debugger->stream, packet); + + ggdwm_exit: + +    va_end(ap); + +    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   : -                                                            * +*                                                                             * +******************************************************************************/ + +static char **g_gdb_debugger_get_register_names(const GGdbDebugger *debugger, const char *group, size_t *count) +{ +    return g_gdb_target_get_register_names(debugger->target, group, count); + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = débogueur à consulter.                            * +*                name     = désignation du registre visé.                     * +*                                                                             * +*  Description : Indique la taille associée à un registre donné.              * +*                                                                             * +*  Retour      : Taille en bits, ou 0 si le registre n'a pas été trouvé.      * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static unsigned int g_gdb_debugger_get_register_size(const GGdbDebugger *debugger, const char *name) +{ +    return g_gdb_target_get_register_size(debugger->target, name); + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = débogueur à consulter.                            * +*                reg      = désignation humaine du register à consulter.      * +*                size     = taille des données mises en jeu.                  * +*                ...      = emplacement de la valeur lue à conserver. [OUT]   * +*                                                                             * +*  Description : Effectue la lecture d'un registre donné.                     * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_debugger_read_register(GGdbDebugger *debugger, const char *reg, size_t size, ...) +{ +    bool result;                            /* Bilan d'opération à renvoyer*/ +    va_list ap;                             /* Liste variable d'arguments  */ +    uint8_t *val8;                          /* Valeur sur 8 bits           */ +    uint16_t *val16;                        /* Valeur sur 16 bits          */ +    uint32_t *val32;                        /* Valeur sur 32 bits          */ +    uint64_t *val64;                        /* Valeur sur 64 bits          */ + +    va_start(ap, size); + +    switch (size) +    { +        case 8: +            val8 = va_arg(ap, uint8_t *); +            result = g_gdb_target_read_register(debugger->target, debugger->stream, debugger->endian, +                                                reg, 8, val8); +            break; + +        case 16: +            val16 = va_arg(ap, uint16_t *); +            result = g_gdb_target_read_register(debugger->target, debugger->stream, debugger->endian, +                                                reg, 16, val16); +            break; + +        case 32: +            val32 = va_arg(ap, uint32_t *); +            result = g_gdb_target_read_register(debugger->target, debugger->stream, debugger->endian, +                                                reg, 32, val32); +            break; + +        case 64: +            val64 = va_arg(ap, uint64_t *); +            result = g_gdb_target_read_register(debugger->target, debugger->stream, debugger->endian, +                                                reg, 64, val64); +            break; + +        default: +            assert(false); +            result = false; +            break; + +    } + +    va_end(ap); + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = débogueur à manipuler.                            * +*                reg      = désignation humaine du register à consulter.      * +*                size     = taille des données mises en jeu.                  * +*                ...      = emplacement de la valeur à écrire.                * +*                                                                             * +*  Description : Effectue l'écriture d'un registre donné.                     * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_debugger_write_register(GGdbDebugger *debugger, const char *reg, size_t size, ...) +{ +    bool result;                            /* Bilan d'opération à renvoyer*/ +    va_list ap;                             /* Liste variable d'arguments  */ +    const uint8_t *val8;                    /* Valeur sur 8 bits           */ +    const uint16_t *val16;                  /* Valeur sur 16 bits          */ +    const uint32_t *val32;                  /* Valeur sur 32 bits          */ +    const uint64_t *val64;                  /* Valeur sur 64 bits          */ + +    va_start(ap, size); + +    switch (size) +    { +        case 8: +            val8 = va_arg(ap, const uint8_t *); +            result = g_gdb_target_write_register(debugger->target, debugger->stream, debugger->endian, +                                                 reg, 8, val8); +            break; + +        case 16: +            val16 = va_arg(ap, const uint16_t *); +            result = g_gdb_target_write_register(debugger->target, debugger->stream, debugger->endian, +                                                 reg, 16, val16); +            break; + +        case 32: +            val32 = va_arg(ap, const uint32_t *); +            result = g_gdb_target_write_register(debugger->target, debugger->stream, debugger->endian, +                                                 reg, 32, val32); +            break; + +        case 64: +            val64 = va_arg(ap, const uint64_t *); +            result = g_gdb_target_write_register(debugger->target, debugger->stream, debugger->endian, +                                                 reg, 64, val64); +            break; + +        default: +            assert(false); +            result = false; +            break; + +    } + +    va_end(ap); + +    return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/*                           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   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_debugger_get_current_pc(GGdbDebugger *debugger, virt_t *pc) +{ +    bool result;                            /* Bilan à retourner           */ + +    result = debugger->ops->get_pc(debugger, pc); + +    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   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_debugger_compute_call_stack(GGdbDebugger *debugger, virt_t **callstack, size_t *size) +{ +    bool result;                            /* Bilan global à retourner    */ + +    if (debugger->ops->compute_cstack != NULL) +        result = debugger->ops->compute_cstack(debugger, callstack, size); + +    else +        result = false; + +    return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/*                             GESTION DES POINTS D'ARRET                             */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +*                                                                             * +*  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      : Structure de suivi mise en place pour l'occasion, voire NULL.* +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static gdb_breakpoint *g_gdb_debugger_enable_memory_breakpoint(GGdbDebugger *debugger, virt_t addr) +{ +    gdb_breakpoint *result;                 /* Nouveau suivi à retourner   */ +    char cmd[3 + VMPA_MAX_LEN + 3];         /* Commande à émettre          */ +    bool status;                            /* Bilan d'une opération       */ +    const char *kind;                       /* Taille spécifique du point  */ +    GGdbPacket *packet;                     /* Paquet de communication     */ +    const char *data;                       /* Données reçues à analyser   */ +    size_t len;                             /* Quantité de données reçues  */ +    GBinaryDebugger *dbg;                   /* Autre version du débogueur  */ +    const uint8_t *bp;                      /* Données du point d'arrêt    */ +    size_t bp_len;                          /* Quantité de ces données     */ +    uint8_t memory[16];                     /* Sauvegarde de la mémoire    */ + +    result = NULL; + +    /* Si l'utilisation de la commande dédiée est possible */ +    if (1)  //////// TODO +    { +        /* Envoi de la requête */ + +        strcpy(cmd, "Z0,"); + +        status = translate_virt_to_hex(debugger, addr, &cmd[3]); + +        if (!status) +            goto ggdemb_exit; + +        kind = debugger->ops->get_bp_kind(debugger, addr); + +        if (kind == NULL) +            goto ggdemb_exit; + +        strcat(cmd, kind); + +        packet = g_gdb_stream_get_free_packet(debugger->stream); + +        g_gdb_packet_start_new_command(packet); +        g_gdb_packet_append(packet, cmd); + +        status = g_gdb_stream_send_packet(debugger->stream, packet); + +        g_gdb_stream_mark_packet_as_free(debugger->stream, packet); + +        if (!status) +            goto ggdemb_exit; + +        /* Réception de la réponse */ + +        packet = g_gdb_stream_recv_packet(debugger->stream); + +        g_gdb_packet_get_data(packet, &data, &len, NULL); + +        if (is_error_code(data, len)) +        { +            g_gdb_stream_mark_packet_as_free(debugger->stream, packet); +            goto ggdemb_fallback; +        } + +        if (strcmp(data, "OK") != 0) +        { +            g_gdb_stream_mark_packet_as_free(debugger->stream, packet); +            goto ggdemb_fallback; +        } + +        g_gdb_stream_mark_packet_as_free(debugger->stream, packet); + +        /* Constitution d'un dossier de suivi */ + +        result = (gdb_breakpoint *)malloc(sizeof(gdb_breakpoint)); + +        result->is_z = true; + +        result->kind = kind; + +    } + +    else +    { + + ggdemb_fallback: + +        dbg = G_BINARY_DEBUGGER(debugger); + +        /* Détermination du point d'arrêt */ + +        bp = debugger->ops->get_bp_data(debugger, addr, &bp_len); + +        assert(bp_len <= 16); + +        /* Sauvegarde de la mémoire courante */ + +        status = g_binary_debugger_read_memory_data(dbg, addr, memory, bp_len); + +        if (!status) goto ggdemb_exit; + +        /* Application du point d'arrêt */ + +        status = g_binary_debugger_write_memory_data(dbg, addr, bp, bp_len); + +        if (!status) goto ggdemb_exit; + +        /* Constitution d'un dossier de suivi */ + +        result = (gdb_breakpoint *)malloc(sizeof(gdb_breakpoint)); + +        result->is_z = false; + +        memcpy(result->memory, memory, bp_len); +        result->len = bp_len; + +    } + +    init_raw_breakpoint((raw_breakpoint *)result, addr); + + ggdemb_exit: + +    return result; +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = débogueur à manipuler ici.                        * +*                bp       = point d'arrêt à traiter.                          * +*                                                                             * +*  Description : Retire un point d'arrêt basique de la mémoire ciblée.        * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_debugger_disable_memory_breakpoint(GGdbDebugger *debugger, gdb_breakpoint *bp) +{ +    bool result;                            /* Bilan à retourner           */ +    char cmd[3 + VMPA_MAX_LEN + 3];         /* Commande à émettre          */ +    bool status;                            /* Bilan d'une opération       */ +    GGdbPacket *packet;                     /* Paquet de communication     */ +    const char *data;                       /* Données reçues à analyser   */ +    size_t len;                             /* Quantité de données reçues  */ +    GBinaryDebugger *dbg;                   /* Autre version du débogueur  */ + +    result = false; + +    /* Si l'utilisation de la commande dédiée est requise */ +    if (bp->is_z) +    { +        /* Envoi de la requête */ + +        strcpy(cmd, "z0,"); + +        status = translate_virt_to_hex(debugger, bp->raw.addr, &cmd[3]); + +        if (!status) +            goto ggddmb_exit; + +        strcat(cmd, bp->kind); + +        packet = g_gdb_stream_get_free_packet(debugger->stream); + +        g_gdb_packet_start_new_command(packet); +        g_gdb_packet_append(packet, cmd); + +        status = g_gdb_stream_send_packet(debugger->stream, packet); + +        g_gdb_stream_mark_packet_as_free(debugger->stream, packet); + +        if (!status) +            goto ggddmb_exit; + +        /* Réception de la réponse */ + +        packet = g_gdb_stream_recv_packet(debugger->stream); + +        g_gdb_packet_get_data(packet, &data, &len, NULL); + +        if (is_error_code(data, len)) +        { +            g_gdb_stream_mark_packet_as_free(debugger->stream, packet); +            goto ggddmb_exit; +        } + +        if (strcmp(data, "OK") != 0) +        { +            g_gdb_stream_mark_packet_as_free(debugger->stream, packet); +            goto ggddmb_exit; +        } + +        g_gdb_stream_mark_packet_as_free(debugger->stream, packet); + +        result = true; + +    } + +    else +    { +        dbg = G_BINARY_DEBUGGER(debugger); + +        result = g_binary_debugger_write_memory_data(dbg, bp->raw.addr, bp->memory, bp->len); + +    } + + ggddmb_exit: + +    return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/*                            CONTROLE DU FLOT D'EXECUTION                            */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = débogueur à redémarrer.                           * +*                                                                             * +*  Description : Redémarre le processus de débogage lié à un serveur GDB.     * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_debugger_restart(GGdbDebugger *debugger) +{ +    bool result;                            /* Bilan à retourner           */ +    GGdbPacket *packet;                     /* Paquet de communication     */ +    return true; +    /* Envoi de la requête */ + +    packet = g_gdb_stream_get_free_packet(debugger->stream); + +    g_gdb_packet_start_new_command(packet); +    g_gdb_packet_append(packet, "R00"); + +    result = g_gdb_stream_send_packet(debugger->stream, packet); + +    g_gdb_stream_mark_packet_as_free(debugger->stream, packet); + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = débogueur à relancer.                             * +*                                                                             * +*  Description : Remet en marche le débogueur utilisant un serveur GDB.       * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_debugger_resume(GGdbDebugger *debugger) +{ +    bool result;                            /* Bilan à retourner           */ +    //char *id;                               /* Identifiant de thread       */ +    GGdbPacket *packet;                     /* Paquet de communication     */ +    //const char *data;                       /* Données reçues à analyser   */ + +    static bool _twice = false; + + +    if (!_twice && 0) +    { + +    packet = g_gdb_stream_get_free_packet(debugger->stream); + +    g_gdb_packet_start_new_command(packet); +    g_gdb_packet_append(packet, "$QPassSignals:e;10;14;17;1a;1b;1c;21;24;25;2c;4c;"); + +    result = g_gdb_stream_send_packet(debugger->stream, packet); + +    g_gdb_stream_mark_packet_as_free(debugger->stream, packet); + +    if (!result) +        goto ggdhmb_exit; + +    } + + + + + + +    /* Envoi de la requête */ + +    /* +    id = g_gdb_debugger_get_active_thread(debugger); +    if (id == NULL) return false; + +    printf("ID : %s\n", id); +    */ + +    /* +    id = g_gdb_support_get_id(debugger->support); +    if (id == NULL) return false; + +    printf("ID : %s\n", id); +    */ + +    packet = g_gdb_stream_get_free_packet(debugger->stream); + +    g_gdb_packet_start_new_command(packet); +    g_gdb_packet_append(packet, "vCont;c:-1"); +    //g_gdb_packet_append(packet, "vCont;c:p256f.-1"); + + +    /* +    if (_twice) +    { +    g_gdb_packet_start_new_command(packet); +    g_gdb_packet_append(packet, "vCont;c:p"); +    g_gdb_packet_append(packet, id); +    g_gdb_packet_append(packet, "."); +    g_gdb_packet_append(packet, id); +    } +    else +    { +        _twice = true; +    g_gdb_packet_start_new_command(packet); +    g_gdb_packet_append(packet, "vCont;c:p"); +    g_gdb_packet_append(packet, id); +    g_gdb_packet_append(packet, ".-1"); +    } +    */ + + + + + +    result = g_gdb_stream_send_packet(debugger->stream, packet); + +    g_gdb_stream_mark_packet_as_free(debugger->stream, packet); + +    if (!result) +        goto ggdhmb_exit; + +    /* Réception de la réponse */ +    /* +    packet = g_gdb_stream_recv_packet(debugger->stream); + +    g_gdb_packet_get_data(packet, &data, NULL, NULL); + +    printf("Ack cont...\n"); + +    //result = (strcmp(data, "OK") == 0); + +    g_gdb_stream_mark_packet_as_free(debugger->stream, packet); +    */ + ggdhmb_exit: + +    _twice = true; + +    return result; + +} + + + + + + + + + + + + + + +/* ---------------------------------------------------------------------------------- */ +/*                          ACCUEIL D'EVENEMENTS ASYNCHRONES                          */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = instance liée à un débogueur GDB à manipuler.     * +*                signum   = indentifiant du signal concerné.                  * +*                                                                             * +*  Description : Réagit à la réception d'un signal par le programme étudié.   * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +void g_gdb_debugger_receive_signal_reply(GGdbDebugger *debugger, int signum) +{ +    virt_t pc;                              /* Position courante du CPU    */ +    bool status;                            /* Bilan d'une opération       */ +    GBinaryDebugger *base;                  /* Version basique du débogueur*/ + +    base = G_BINARY_DEBUGGER(debugger); + +    status = g_binary_debugger_get_current_pc(base, &pc); + +    if (!status) +        pc = VMPA_NO_VIRTUAL; + +    on_binary_debugger_stopped(base, pc); + +    g_signal_emit_by_name(debugger, "signaled", signum); + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = instance liée à un débogueur GDB à manipuler.     * +*                status   = indication d'état à la sortie.                    * +*                pid      = éventuel identifiant de processus concerné ou -1. * +*                                                                             * +*  Description : Réagit à la sortie d'exécution d'un programme étudié.        * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +void g_gdb_debugger_receive_exit_reply(GGdbDebugger *debugger, int status, pid_t pid) +{ +    GBinaryDebugger *base;                  /* Version basique du débogueur*/ + +    base = G_BINARY_DEBUGGER(debugger); + +    on_binary_debugger_finished(base, pid); + +    g_signal_emit_by_name(debugger, "exited", status, pid); + +} diff --git a/src/debug/remgdb/gdb.h b/src/debug/gdbrsp/gdb.h index 38aa8fe..7faa044 100644 --- a/src/debug/remgdb/gdb.h +++ b/src/debug/gdbrsp/gdb.h @@ -21,8 +21,8 @@   */ -#ifndef _DEBUG_REMGDB_GDB_H -#define _DEBUG_REMGDB_GDB_H +#ifndef _DEBUG_GDBRSP_GDB_H +#define _DEBUG_GDBRSP_GDB_H  #include <glib-object.h> @@ -51,11 +51,11 @@ typedef struct _GGdbDebuggerClass GGdbDebuggerClass;  GType g_gdb_debugger_get_type(void);  /* Crée un débogueur utilisant un serveur GDB distant. */ -GBinaryDebugger *g_gdb_debugger_new(GLoadedBinary *, void *); +GBinaryDebugger *g_gdb_debugger_new(GLoadedBinary *, const char *, unsigned short);  void test_gdb(void); -#endif  /* _DEBUG_REMGDB_GDB_H */ +#endif  /* _DEBUG_GDBRSP_GDB_H */ diff --git a/src/debug/remgdb/helpers.c b/src/debug/gdbrsp/helpers.c index bae7f0b..296d642 100644 --- a/src/debug/remgdb/helpers.c +++ b/src/debug/gdbrsp/helpers.c @@ -28,12 +28,97 @@  #include <string.h> +#include "gdb-int.h" +#include "utils.h" +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = débogueur à consulter.                            * +*                addr     = emplacement en mémoire à venir consulter.         * +*                out      = zone d'impression en hexadécimal. [OUT]           * +*                                                                             * +*  Description : Traduit une adresse en chaîne hexadécimale pour GDB.         * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +bool translate_virt_to_hex(const GGdbDebugger *debugger, virt_t addr, char *out) +{ +    bool result;                            /* Bilan d'opération à renvoyer*/ +    uint8_t conv8;                          /* Valeur adaptée sur 8 bits   */ +    uint16_t conv16;                        /* Valeur adaptée sur 16 bits  */ +    uint32_t conv32;                        /* Valeur adaptée sur 32 bits  */ +    uint64_t conv64;                        /* Valeur adaptée sur 64 bits  */ +    char hexval[17];                        /* Valeur sous forme hexa      */ +    bool got_msn;                           /* Obtention d'un quartet ?    */ +    size_t i;                               /* Boucle de parcours          */ + +    /* Conversion */ + +    switch (debugger->msize) +    { +        case MDS_8_BITS: +            conv8 = addr; +            result = u8_to_hex(&conv8, hexval); +            break; + +        case MDS_16_BITS: +            conv16 = addr; +            conv16 = to_u16(&conv16, SRE_BIG); +            result = u16_to_hex(&conv16, hexval); +            break; + +        case MDS_32_BITS: +            conv32 = addr; +            conv32 = to_u32(&conv32, SRE_BIG); +            result = u32_to_hex(&conv32, hexval); +            break; + +        case MDS_64_BITS: +            conv64 = addr; +            conv64 = to_u64(&conv64, SRE_BIG); +            result = u64_to_hex(&conv64, hexval); +            break; + +        default: +            result = false; +            break; + +    } + +    /* On saute les zéros préliminaires... */ + +    if (result) +    { +        got_msn = false; + +        for (i = 0; i < 17; i++) +        { +            if (!got_msn) +            { +                if (hexval[i] == '0') +                    continue; +                else +                    got_msn = true; +            } +            *out = hexval[i]; +            out++; +        } + +        *out = '\0'; + +    } + +    return result; +} diff --git a/src/debug/remgdb/helpers.h b/src/debug/gdbrsp/helpers.h index d68fc11..5f5f301 100644 --- a/src/debug/remgdb/helpers.h +++ b/src/debug/gdbrsp/helpers.h @@ -21,16 +21,17 @@   */ -#ifndef _DEBUG_REMGDB_HELPERS_H -#define _DEBUG_REMGDB_HELPERS_H +#ifndef _DEBUG_GDBRSP_HELPERS_H +#define _DEBUG_GDBRSP_HELPERS_H +#include "gdb.h"  #include "packet.h" -#include "../../arch/archbase.h" -#include "../../common/endianness.h" +/* Traduit une adresse en chaîne hexadécimale pour GDB. */ +bool translate_virt_to_hex(const GGdbDebugger *, virt_t, char *); @@ -58,4 +59,4 @@ bool get_stop_reply_sig_info(const GGdbPacket *, int *, vmpa_t *, pid_t *, Sourc -#endif  /* _DEBUG_REMGDB_HELPERS_H */ +#endif  /* _DEBUG_GDBRSP_HELPERS_H */ diff --git a/src/debug/gdbrsp/helpers_arm.c b/src/debug/gdbrsp/helpers_arm.c new file mode 100644 index 0000000..e2491ae --- /dev/null +++ b/src/debug/gdbrsp/helpers_arm.c @@ -0,0 +1,252 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * helpers_arm.c - compléments utiles à GDB pour l'architecture ARM + * + * Copyright (C) 2016 Cyrille Bagard + * + *  This file is part of Chrysalide. + * + *  Chrysalide is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 3 of the License, or + *  (at your option) any later version. + * + *  Chrysalide is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with Foobar.  If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "helpers_arm.h" + + +#include <malloc.h> +#include <string.h> + + +#include "gdb-int.h" + + + +/* Détermine le point d'exécution courant. */ +static bool get_arm_pc(GGdbDebugger *, virt_t *); + +/* Remonte la pile d'appels jusqu'au point courant. */ +static bool compute_call_stack_for_arm(const GGdbDebugger *, virt_t **, size_t *); + +/* Complète la commande manipulant des points d'arrêt. */ +static const char *get_breakpoint_kind_for_arm(const GGdbDebugger *, virt_t); + +/* Construit une instruction provoquant un arrêt d'exécution. */ +static const uint8_t *get_arm_breakpoint_data(const GGdbDebugger *, virt_t, size_t *); + + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : -                                                            * +*                                                                             * +*  Description : Fournit les fonctions adaptées aux opérations pour ARM.      * +*                                                                             * +*  Retour      : Opérations spécifiques adaptées à ARM.                       * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +const gdb_arch_ops *get_arm_operations(void) +{ +    static const gdb_arch_ops arm_ops = { + +        .get_pc = get_arm_pc, +        .compute_cstack = compute_call_stack_for_arm, +        .get_bp_kind = get_breakpoint_kind_for_arm, +        .get_bp_data = get_arm_breakpoint_data + +    }; + +    return &arm_ops; + +} + + +/****************************************************************************** +*                                                                             * +*  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   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool get_arm_pc(GGdbDebugger *debugger, virt_t *pc) +{ +    bool result;                            /* Bilan à retourner           */ +    uint32_t value; + +    result = g_binary_debugger_read_register_u32(G_BINARY_DEBUGGER(debugger), "pc", &value); + +    if (result) +        *pc = value; + +    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   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool compute_call_stack_for_arm(const GGdbDebugger *debugger, virt_t **callstack, size_t *size) +{ +    bool result;                            /* Bilan global à retourner    */ +    GBinaryDebugger *base;                  /* Version basique d'instance  */ +    uint32_t lr;                            /* Retour de fonction          */ +    uint32_t fp;                            /* Pointeur de cadre à suivre  */ + +    base = G_BINARY_DEBUGGER(debugger); + +    result = g_binary_debugger_read_register_u32(base, "lr", &lr); + +    if (result && lr != 0) +    { +        *callstack = (virt_t *)realloc(*callstack, ++(*size) * sizeof(virt_t)); + +        (*callstack)[*size - 1] = lr; + +    } + +    result &= g_binary_debugger_read_register_u32(base, "r11", &fp); + +    while (result && fp != 0) +    { +        /** +         * fp[-0] : pc sauvegardé +         * fp[-1] : lr sauvegardé +         * fp[-2] : sp précédent +         * fp[-3] : fp précédent +         */ + +        result = g_binary_debugger_read_memory_u32(base, fp - 2 * sizeof(uint32_t), &lr); +        if (!result) break; + +        if (lr != 0) +        { +            *callstack = (virt_t *)realloc(*callstack, ++(*size) * sizeof(virt_t)); + +            (*callstack)[*size - 1] = lr; + +        } + +        result = g_binary_debugger_read_memory_u32(base, fp - 4 * sizeof(uint32_t), &fp); + +    } + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = débogueur à manipuler ici.                        * +*                virt     = emplacement du point mémoire à traiter.           * +*                                                                             * +*  Description : Complète la commande manipulant des points d'arrêt.          * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static const char *get_breakpoint_kind_for_arm(const GGdbDebugger *debugger, virt_t virt) +{ +    const char *result;                     /* Indication à retourner      */ +    GArchProcessor *proc;                   /* Processeur lié au binaire   */ +    vmpa2t addr;                            /* Format d'adresse complet    */ +    GArchInstruction *instr;                /* Instruction ciblée          */ +    const char *encoding;                   /* Encodage de l'instruction   */ + +    proc = g_loaded_binary_get_processor(G_BINARY_DEBUGGER(debugger)->binary); + +    init_vmpa(&addr, VMPA_NO_PHYSICAL, virt); +    instr = g_arch_processor_find_instr_by_address(proc, &addr); + +    if (instr == NULL) +        result = NULL; + +    else +    { +        encoding = g_arch_instruction_get_encoding(instr); + +        if (strcmp(encoding, "Thumb/16") == 0) +            result = ",2"; + +        if (strcmp(encoding, "Thumb/32") == 0) +            result = ",3"; + +        if (strcmp(encoding, "ARM") == 0) +            result = ",4"; + +        else +            result = NULL; + +        g_object_unref(G_OBJECT(instr)); + +    } + +    g_object_unref(G_OBJECT(proc)); + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = débogueur à manipuler ici.                        * +*                addr     = emplacement du point mémoire à traiter.           * +*                len      = quantité de mémoire à remplacer. [OUT]            * +*                                                                             * +*  Description : Construit une instruction provoquant un arrêt d'exécution.   * +*                                                                             * +*  Retour      : Définition du point d'arrêt à placer à l'adresse donnée.     * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static const uint8_t *get_arm_breakpoint_data(const GGdbDebugger *debugger, virt_t addr, size_t *len) +{ +    const uint8_t *result;                  /* Données à placer en mémoire */ + +    /* Version point d'arrêt */ +    static const uint32_t bkpt_code[] = { 0xe1200070 }; + +    *len = sizeof(bkpt_code);; + +    result = (const uint8_t *)bkpt_code; + +    return result; + +} diff --git a/src/debug/gdbrsp/helpers_arm.h b/src/debug/gdbrsp/helpers_arm.h new file mode 100644 index 0000000..70977bc --- /dev/null +++ b/src/debug/gdbrsp/helpers_arm.h @@ -0,0 +1,37 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * helpers_arm.h - prototypes pour les compléments utiles à GDB pour l'architecture ARM + * + * Copyright (C) 2016 Cyrille Bagard + * + *  This file is part of Chrysalide. + * + *  Chrysalide is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 3 of the License, or + *  (at your option) any later version. + * + *  Chrysalide is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with Foobar.  If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _DEBUG_GDBRSP_HELPERS_ARM_H +#define _DEBUG_GDBRSP_HELPERS_ARM_H + + +#include "aops.h" + + + +/* Fournit les fonctions adaptées aux opérations pour ARM. */ +const gdb_arch_ops *get_arm_operations(void); + + + +#endif  /* _DEBUG_GDBRSP_HELPERS_ARM_H */ diff --git a/src/debug/gdbrsp/helpers_arm64.c b/src/debug/gdbrsp/helpers_arm64.c new file mode 100644 index 0000000..6807662 --- /dev/null +++ b/src/debug/gdbrsp/helpers_arm64.c @@ -0,0 +1,97 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * helpers_arm64.c - compléments utiles à GDB pour l'architecture AArch64 + * + * Copyright (C) 2016 Cyrille Bagard + * + *  This file is part of Chrysalide. + * + *  Chrysalide is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 3 of the License, or + *  (at your option) any later version. + * + *  Chrysalide is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with Foobar.  If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "helpers_arm64.h" + + +#include <malloc.h> + + +#include "gdb-int.h" + + + +/****************************************************************************** +*                                                                             * +*  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 compute_call_stack_for_arm64(GGdbDebugger *debugger, virt_t **callstack, size_t *size) +{ +    bool result;                            /* Bilan global à retourner    */ +    GBinaryDebugger *base;                  /* Version basique d'instance  */ +    uint64_t fp;                            /* Pointeur de cadre à suivre  */ +    uint64_t previous;                      /* Appel de fonction précédent */ + +    base = G_BINARY_DEBUGGER(debugger); + +    result = g_binary_debugger_read_register_u64(base, "x29", &fp); + +    while (result && fp != 0) +    { +        result = g_binary_debugger_read_memory_u64(base, fp + sizeof(uint64_t), &previous); +        if (!result) break; + +        *callstack = (virt_t *)realloc(*callstack, ++(*size) * sizeof(virt_t)); + +        (*callstack)[*size - 1] = previous; + +        result = g_binary_debugger_read_memory_u64(base, fp, &fp); + +    } + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : debugger = débogueur à manipuler ici.                        * +*                addr     = emplacement du point mémoire à traiter.           * +*                cmd      = commande en cours de constitution. [OUT]          * +*                                                                             * +*  Description : Complète la commande manipulant des points d'arrêt.          * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +bool fill_memory_breakpoint_cmd_for_arm64(GGdbDebugger *debugger, virt_t addr, char *cmd) +{ +    strcat(cmd, ",4"); + +    return true; + +} diff --git a/src/debug/gdbrsp/helpers_arm64.h b/src/debug/gdbrsp/helpers_arm64.h new file mode 100644 index 0000000..80f9312 --- /dev/null +++ b/src/debug/gdbrsp/helpers_arm64.h @@ -0,0 +1,40 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * helpers_arm64.h - prototypes pour les compléments utiles à GDB pour l'architecture AArch64 + * + * Copyright (C) 2016 Cyrille Bagard + * + *  This file is part of Chrysalide. + * + *  Chrysalide is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 3 of the License, or + *  (at your option) any later version. + * + *  Chrysalide is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with Foobar.  If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _DEBUG_GDBRSP_HELPERS_ARM64_H +#define _DEBUG_GDBRSP_HELPERS_ARM64_H + + +#include "gdb.h" + + + +/* Remonte la pile d'appels jusqu'au point courant. */ +bool compute_call_stack_for_arm64(GGdbDebugger *, virt_t **, size_t *); + +/* Complète la commande manipulant des points d'arrêt. */ +bool fill_memory_breakpoint_cmd_for_arm64(GGdbDebugger *, virt_t, char *); + + + +#endif  /* _DEBUG_GDBRSP_HELPERS_ARM64_H */ diff --git a/src/debug/remgdb/packet.c b/src/debug/gdbrsp/packet.c index 8d798c6..4a70a79 100644 --- a/src/debug/remgdb/packet.c +++ b/src/debug/gdbrsp/packet.c @@ -174,13 +174,15 @@ void g_gdb_packet_append(GGdbPacket *packet, const char *string)      len = strlen(string);      /* Si la place n'est pas assez grande */ -    if ((packet->len + len + 1) >= packet->allocated) +    if ((packet->len + len + 1) > packet->allocated)      {          packet->buffer = (char *)realloc(packet->buffer, (packet->len + len + 1) * sizeof(char));          packet->allocated = packet->len + len + 1;      } -    strcat(packet->buffer, string); + +    memcpy(packet->buffer + packet->len, string, len + 1); +    //strcat(packet->buffer, string);      packet->len += len; @@ -309,6 +311,8 @@ bool g_gdb_packet_decode(GGdbPacket *packet)      if (packet->buffer != NULL)          free(packet->buffer); +    buffer[k] = '\0'; +      packet->buffer = buffer;      packet->len = k;      packet->allocated = allocated; @@ -322,7 +326,7 @@ bool g_gdb_packet_decode(GGdbPacket *packet)  *                                                                             *  *  Paramètres  : packet   = paquet à analyser.                                *  *                data     = données contenues dans le paquet. [OUT]           * -*                len      = quantité de ces données. [OUT]                    * +*                len      = quantité de ces données ou NULL. [OUT]            *  *                checksum = contrôle d'intégrité des données ou NULL. [OUT]   *  *                                                                             *  *  Description : Fournit le contenu du paquet.                                * @@ -336,7 +340,9 @@ bool g_gdb_packet_decode(GGdbPacket *packet)  void g_gdb_packet_get_data(const GGdbPacket *packet, const char **data, size_t *len, uint8_t *checksum)  {      *data = packet->buffer; -    *len = packet->len; + +    if (len != NULL) +        *len = packet->len;      if (checksum != NULL)          *checksum = packet->checksum; diff --git a/src/debug/remgdb/packet.h b/src/debug/gdbrsp/packet.h index 9c44108..2e8abb7 100644 --- a/src/debug/remgdb/packet.h +++ b/src/debug/gdbrsp/packet.h @@ -21,8 +21,8 @@   */ -#ifndef _DEBUG_REMGDB_PACKET_H -#define _DEBUG_REMGDB_PACKET_H +#ifndef _DEBUG_GDBRSP_PACKET_H +#define _DEBUG_GDBRSP_PACKET_H  #include <glib-object.h> @@ -79,4 +79,4 @@ GGdbPacket *g_gdb_packet_pop(GGdbPacket **); -#endif  /* _DEBUG_REMGDB_PACKET_H */ +#endif  /* _DEBUG_GDBRSP_PACKET_H */ diff --git a/src/debug/remgdb/stream-int.h b/src/debug/gdbrsp/stream-int.h index 8ac422d..db61d13 100644 --- a/src/debug/remgdb/stream-int.h +++ b/src/debug/gdbrsp/stream-int.h @@ -21,10 +21,11 @@   */ -#ifndef _DEBUG_REMGDB_STREAM_INT_H -#define _DEBUG_REMGDB_STREAM_INT_H +#ifndef _DEBUG_GDBRSP_STREAM_INT_H +#define _DEBUG_GDBRSP_STREAM_INT_H +#include "gdb.h"  #include "stream.h" @@ -43,6 +44,8 @@ struct _GGdbStream      int fd;                                 /* Flux ouvert en L./E.        */ +    GGdbDebugger *owner;                    /* Propriétaire du flux        */ +      send_gdb_data_fc send_data;             /* Envoi d'un paquet GDB       */      recv_gdb_byte_fc recv_byte;             /* Réception d'un paquet GDB   */ @@ -55,6 +58,18 @@ struct _GGdbStream      GCond recv_cond;                        /* Attente de disponibilité    */      GMutex recv_mutex;                      /* Accès à la liste            */ +    GGdbPacket *status_packets;             /* Liste des paquets d'état    */ +    GCond status_cond;                      /* Attente de disponibilité    */ +    GMutex status_mutex;                    /* Accès à la liste            */ + + + + +    bool skip_ack; + +    bool want_status; + +  }; @@ -71,4 +86,4 @@ bool g_gdb_stream_listen(GGdbStream *); -#endif  /* _DEBUG_REMGDB_STREAM_INT_H */ +#endif  /* _DEBUG_GDBRSP_STREAM_INT_H */ diff --git a/src/debug/remgdb/stream.c b/src/debug/gdbrsp/stream.c index cd303c3..979ed9b 100644 --- a/src/debug/remgdb/stream.c +++ b/src/debug/gdbrsp/stream.c @@ -24,14 +24,19 @@  #include "stream.h" +#include <assert.h>  #include <stdio.h>  #include <stdlib.h> +#include <strings.h>  #include <glib/gthread.h>  #include <sys/select.h> +#include "gdb-int.h"  #include "stream-int.h" +#include "utils.h"  #include "../../common/dllist.h" +#include "../../gui/panels/log.h" @@ -41,12 +46,21 @@ static void g_gdb_stream_class_init(GGdbStreamClass *);  /* Initialise une instance de flux de communication avec GDB. */  static void g_gdb_stream_init(GGdbStream *); +/* Supprime toutes les références externes. */ +static void g_gdb_stream_dispose(GGdbStream *); + +/* Procède à la libération totale de la mémoire. */ +static void g_gdb_stream_finalize(GGdbStream *); +  /* Envoie un acquittement pour la dernière réception. */  static bool gdb_stream_ack(GGdbStream *);  /* Ecoute une connexion à un serveur GDB. */  static void *gdb_stream_thread(GGdbStream *); +/* Reste en alerte quant au changement de statut de l'exécution. */ +static void *gdb_stream_status_thread(GGdbStream *); +  /* Réceptionne un paquet d'un serveur GDB. */  static bool g_gdb_stream_read_packet(GGdbStream *, GGdbPacket *); @@ -70,6 +84,12 @@ G_DEFINE_TYPE(GGdbStream, g_gdb_stream, G_TYPE_OBJECT);  static void g_gdb_stream_class_init(GGdbStreamClass *klass)  { +    GObjectClass *object;                   /* Autre version de la classe  */ + +    object = G_OBJECT_CLASS(klass); + +    object->dispose = (GObjectFinalizeFunc/* ! */)g_gdb_stream_dispose; +    object->finalize = (GObjectFinalizeFunc)g_gdb_stream_finalize;  } @@ -93,11 +113,86 @@ static void g_gdb_stream_init(GGdbStream *stream)      g_cond_init(&stream->recv_cond);      g_mutex_init(&stream->recv_mutex); +    g_cond_init(&stream->status_cond); +    g_mutex_init(&stream->status_mutex); + +    stream->skip_ack = false; + +    stream->want_status = false; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : stream = instance d'objet GLib à traiter.                    * +*                                                                             * +*  Description : Supprime toutes les références externes.                     * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void g_gdb_stream_dispose(GGdbStream *stream) +{ +    g_object_unref(G_OBJECT(stream->owner)); + + +    /* TODO... */ + + +    G_OBJECT_CLASS(g_gdb_stream_parent_class)->dispose(G_OBJECT(stream)); + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : stream = instance d'objet GLib à traiter.                    * +*                                                                             * +*  Description : Procède à la libération totale de la mémoire.                * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void g_gdb_stream_finalize(GGdbStream *stream) +{ + +    /* TODO */ + + +    G_OBJECT_CLASS(g_gdb_stream_parent_class)->finalize(G_OBJECT(stream)); +  }  /******************************************************************************  *                                                                             * +*  Paramètres  : stream = instance à modifier.                                * +*                                                                             * +*  Description : Ne participe plus aux acquitements de paquets.               * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +void g_gdb_stream_do_not_ack(GGdbStream *stream) +{ +    stream->skip_ack = true; + +} + + + +/****************************************************************************** +*                                                                             *  *  Paramètres  : stream = instance à réellement lancer.                       *  *                                                                             *  *  Description : Lance l'écoute d'un flux de communication avec GDB.          * @@ -117,6 +212,9 @@ bool g_gdb_stream_listen(GGdbStream *stream)      if (!g_thread_new("chrysalide_gdb_stream", (GThreadFunc)gdb_stream_thread, stream))          result = false; +    if (!g_thread_new("chrysalide_gdb_status", (GThreadFunc)gdb_stream_status_thread, stream)) +        result = false; +      return result;  } @@ -136,6 +234,9 @@ bool g_gdb_stream_listen(GGdbStream *stream)  static bool gdb_stream_ack(GGdbStream *stream)  { +    /// +    //return true; +      bool result;                            /* Bilan à retourner           */      GGdbPacket *packet;                     /* Paquet à envoyer            */ @@ -171,6 +272,12 @@ static void *gdb_stream_thread(GGdbStream *stream)      int ret;                                /* Bilan d'un appel            */      GGdbPacket *packet;                     /* Nouveau paquet reçu         */ + +    const char *data;                       /* Données reçues à analyser   */ +    size_t len;                             /* Quantité de ces données     */ + + +      while (1)      {          FD_ZERO(&rfds); @@ -178,8 +285,6 @@ static void *gdb_stream_thread(GGdbStream *stream)          ret = select(stream->fd + 1, &rfds, NULL, NULL, NULL); -        printf("ret :: %d\n", ret); -          switch (ret)          {              case -1: @@ -197,10 +302,46 @@ static void *gdb_stream_thread(GGdbStream *stream)                  if (g_gdb_stream_read_packet(stream, packet))                  { -                    /* Acquittement */ -                    if (!gdb_stream_ack(stream)) goto bad_recv; +                    /* Acquittement ? */ +                    if (!stream->skip_ack) +                    { +                        if (!gdb_stream_ack(stream)) goto bad_recv; +                    } + + +                    /* On conserve le résultat ? */ + + +                    g_gdb_packet_get_data(packet, &data, &len, NULL); + + +                    //printf("---------------------------\n"); +                    //printf(">> want status ? %d\n", stream->want_status); +                    //printf(">> got '%s'\n", data); + + +                    if (len >= 1) +                    { +                        if (stream->want_status) +                            stream->want_status = false; + +                        else if (index("STWX", data[0]) != NULL) +                        { +                            g_mutex_lock(&stream->status_mutex); +                            g_gdb_packet_push(&stream->status_packets, packet); +                            g_mutex_unlock(&stream->status_mutex); + +                            g_cond_signal(&stream->status_cond); + +                            break; +                        } + +                        // else message inconnu -> log_message() ! + +                    } + + -                    /* On conserve le résultat */                      g_mutex_lock(&stream->recv_mutex);                      g_gdb_packet_push(&stream->recv_packets, packet); @@ -210,6 +351,11 @@ static void *gdb_stream_thread(GGdbStream *stream)                  } +                else +                    g_gdb_stream_mark_packet_as_free(stream, packet); + +                break; +   bad_recv:                  printf("bad things happend...\n"); @@ -222,6 +368,115 @@ static void *gdb_stream_thread(GGdbStream *stream)      } + +    printf("Oh noes....\n"); + + +    return NULL; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : stream = encadrement associée à l'opération.                 * +*                                                                             * +*  Description : Reste en alerte quant au changement de statut de l'exécution.* +*                                                                             * +*  Retour      : ???                                                          * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void *gdb_stream_status_thread(GGdbStream *stream) +{ +    GGdbPacket *packet;                     /* Nouveau paquet reçu         */ +    const char *data;                       /* Données reçues à analyser   */ +    size_t len;                             /* Quantité de ces données     */ +    bool malformed;                         /* Echec d'interprétation      */ +    uint8_t byte;                           /* Valeur quelconque sur 8 bits*/ +    bool ret;                               /* Bilan d'un appel            */ + +    while (1) +    { +        /* Réception d'un nouveau paquet de statut */ + +        g_mutex_lock(&stream->status_mutex); + +        if (dl_list_empty(stream->status_packets)) +            g_cond_wait(&stream->status_cond, &stream->status_mutex); + +        packet = g_gdb_packet_pop(&stream->status_packets); + +        g_mutex_unlock(&stream->status_mutex); + +        /* Traitement du paquet reçu */ + +        g_gdb_packet_get_data(packet, &data, &len, NULL); + +        malformed = false; + +        switch (data[0]) +        { +            case 'S': + +                ret = read_fixed_byte(data + 1, len - 1, &byte); + +                if (!ret) +                { +                    malformed = true; +                    goto gsst_processed; +                } + +                g_gdb_debugger_receive_signal_reply(stream->owner, byte); +                break; + +            case 'T': +                assert(false);  // TODO +                break; + +            case 'W': + +                ret = read_fixed_byte(data + 1, len - 1, &byte); + +                if (!ret) +                { +                    malformed = true; +                    goto gsst_processed; +                } + + +                // TODO : ";process:pid" + + +                printf("Program exited (status=%hhu)\n", byte); + + +                g_gdb_debugger_receive_exit_reply(stream->owner, byte, -1); + + +                // log_message en cas de mauvais format... + + +                break; + + +            default: +                assert(false); +                break; + +        } + + gsst_processed: + +        if (malformed && true/* TODO : config->show_... */) +            log_variadic_message(LMT_WARNING, "Malformed GDB status reply: '%s'", data); + +        g_gdb_stream_mark_packet_as_free(stream, packet); + +    } +      return NULL;  } @@ -253,6 +508,9 @@ GGdbPacket *g_gdb_stream_get_free_packet(GGdbStream *stream)      g_mutex_unlock(&stream->free_mutex); +    // ??? +    //g_gdb_packet_start_new_command(result); +      return result;  } @@ -273,6 +531,10 @@ GGdbPacket *g_gdb_stream_get_free_packet(GGdbStream *stream)  void g_gdb_stream_mark_packet_as_free(GGdbStream *stream, GGdbPacket *packet)  { +    //// Utile ? +    g_gdb_packet_start_new_command(packet); + +      g_mutex_lock(&stream->free_mutex);      g_gdb_packet_push(&stream->free_packets, packet); @@ -301,7 +563,12 @@ static bool g_gdb_stream_read_packet(GGdbStream *stream, GGdbPacket *packet)      char tmp[3];                            /* Tampon de réception         */      uint8_t checksum;                       /* Contrôle d'intégrité        */ -    result = stream->recv_byte(stream, tmp); +    do +    { +        result = stream->recv_byte(stream, tmp); +        if (tmp[0] != '+') break; +    } +    while (0);      if (tmp[0] != '$') return false; @@ -309,6 +576,8 @@ static bool g_gdb_stream_read_packet(GGdbStream *stream, GGdbPacket *packet)      while ((result = stream->recv_byte(stream, tmp)))      { +        //printf(" .. '%c'\n", tmp[0]); +          if (tmp[0] == '#') break;          else g_gdb_packet_append(packet, tmp);      } @@ -346,7 +615,7 @@ static bool g_gdb_stream_read_packet(GGdbStream *stream, GGdbPacket *packet)  *  Remarques   : -                                                            *  *                                                                             *  ******************************************************************************/ - +#include <string.h>  bool g_gdb_stream_send_packet(GGdbStream *stream, GGdbPacket *packet)  {      bool result;                            /* Bilan à renvoyer            */ @@ -355,18 +624,43 @@ bool g_gdb_stream_send_packet(GGdbStream *stream, GGdbPacket *packet)      uint8_t checksum;                       /* Contrôle d'intégrité        */      char tmp[3];                            /* Impression du checksum      */ +    g_gdb_packet_get_data(packet, &data, &len, NULL); + +#if 1 +    /* Ack ? */ +    if (len == 1 && data[0] == '+') +        result = stream->send_data(stream, "+", 1); + +    else +#endif +    { +      result = stream->send_data(stream, "$", 1); +    //result = stream->send_data(stream, "+$", 2);      g_gdb_packet_compute_checksum(packet);      g_gdb_packet_get_data(packet, &data, &len, &checksum); + +    if (len == 1 && data[0] == '?') +        stream->want_status = true; + +    /* +    if (memcmp(data, "vCont;c", strlen("vCont;c")) == 0) +        stream->want_status = true; +    */ + + +      result &= stream->send_data(stream, data, len);      result = stream->send_data(stream, "#", 1); -    snprintf(tmp, 3, "%hhx", checksum); +    snprintf(tmp, 3, "%02hhx", checksum);      result &= stream->send_data(stream, tmp, 2); +    } +      return result;  } diff --git a/src/debug/remgdb/stream.h b/src/debug/gdbrsp/stream.h index f93ff60..b4b483d 100644 --- a/src/debug/remgdb/stream.h +++ b/src/debug/gdbrsp/stream.h @@ -21,8 +21,8 @@   */ -#ifndef _DEBUG_REMGDB_STREAM_H -#define _DEBUG_REMGDB_STREAM_H +#ifndef _DEBUG_GDBRSP_STREAM_H +#define _DEBUG_GDBRSP_STREAM_H  #include "packet.h" @@ -48,6 +48,9 @@ typedef struct _GGdbStreamClass GGdbStreamClass;  /* Indique le type défini pour un flux de communication avec un serveur GDB. */  GType g_gdb_stream_get_type(void); +/* Ne participe plus aux acquitements de paquets. */ +void g_gdb_stream_do_not_ack(GGdbStream *); +  /* Fournit un paquet prêt à emploi. */  GGdbPacket *g_gdb_stream_get_free_packet(GGdbStream *); @@ -62,4 +65,4 @@ GGdbPacket *g_gdb_stream_recv_packet(GGdbStream *); -#endif  /* _DEBUG_REMGDB_STREAM_H */ +#endif  /* _DEBUG_GDBRSP_STREAM_H */ diff --git a/src/debug/gdbrsp/support.c b/src/debug/gdbrsp/support.c new file mode 100644 index 0000000..5086540 --- /dev/null +++ b/src/debug/gdbrsp/support.c @@ -0,0 +1,598 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * support.c - conformité dans l'interfaçage client/serveur + * + * Copyright (C) 2016 Cyrille Bagard + * + *  This file is part of Chrysalide. + * + *  Chrysalide is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 3 of the License, or + *  (at your option) any later version. + * + *  Chrysalide is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with Foobar.  If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "support.h" + + +#include <stdlib.h> +#include <string.h> + + + +/* Indications quant à l'interfaçage client/serveur GDB (instance) */ +struct _GGdbSupport +{ +    GObject parent;                         /* A laisser en premier        */ + +    unsigned long packet_size;              /* Taille maximale d'un paquet */ + +    bool os_data; + + +    bool extended_mode;                     /* Mode étendu présent & actif */ + + +    char *id; + +}; + +/* Indications quant à l'interfaçage client/serveur GDB (classe) */ +struct _GGdbSupportClass +{ +    GObjectClass parent;                    /* A laisser en premier        */ + +}; + + +/* Initialise la classe des détails d'interfaçage GDB. */ +static void g_gdb_support_class_init(GGdbSupportClass *); + +/* Procède à l'initialisation des détails d'interfaçage GDB. */ +static void g_gdb_support_init(GGdbSupport *); + +/* Supprime toutes les références externes. */ +static void g_gdb_support_dispose(GGdbSupport *); + +/* Procède à la libération totale de la mémoire. */ +static void g_gdb_support_finalize(GGdbSupport *); + +/* Lit une valeur booléenne à partir des détails du serveur. */ +static bool g_gdb_support_read_bool(GGdbSupport *, const char *, const char *, bool *); + +/* Lit une valeur longue à partir des détails du serveur. */ +static bool g_gdb_support_read_ulong(GGdbSupport *, const char *, const char *, unsigned long *); + + + +/* Indique le type défini par la GLib pour les détails d'interfaçage GDB. */ +G_DEFINE_TYPE(GGdbSupport, g_gdb_support, G_TYPE_OBJECT); + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : klass = classe de débogueur à initialiser.                   * +*                                                                             * +*  Description : Initialise la classe des détails d'interfaçage GDB.          * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void g_gdb_support_class_init(GGdbSupportClass *klass) +{ +    GObjectClass *object;                   /* Autre version de la classe  */ + +    object = G_OBJECT_CLASS(klass); + +    object->dispose = (GObjectFinalizeFunc/* ! */)g_gdb_support_dispose; +    object->finalize = (GObjectFinalizeFunc)g_gdb_support_finalize; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : support = instance de débogueur à préparer.                  * +*                                                                             * +*  Description : Procède à l'initialisation des détails d'interfaçage GDB.    * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void g_gdb_support_init(GGdbSupport *support) +{ + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : support = instance d'objet GLib à traiter.                   * +*                                                                             * +*  Description : Supprime toutes les références externes.                     * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void g_gdb_support_dispose(GGdbSupport *support) +{ +    G_OBJECT_CLASS(g_gdb_support_parent_class)->dispose(G_OBJECT(support)); + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : support = instance d'objet GLib à traiter.                   * +*                                                                             * +*  Description : Procède à la libération totale de la mémoire.                * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void g_gdb_support_finalize(GGdbSupport *support) +{ +    G_OBJECT_CLASS(g_gdb_support_parent_class)->finalize(G_OBJECT(support)); + +} + + + + + + + + + + + + + + +#include <string.h> + +static char *build_id(GGdbStream *stream) +{ +    char *result;                           /* Identifiant à renvoyer      */ +    GGdbPacket *packet;                     /* Paquet de communication     */ +    bool status;                            /* Bilan d'une communication   */ +    const char *data;                       /* Données reçues à analyser   */ +    const char *start;                      /* Début d'identification      */ +    const char *end;                        /* Fin d'identification        */ + +    result = NULL; + +    /* Envoi de la requête */ + +    packet = g_gdb_stream_get_free_packet(stream); + +    g_gdb_packet_start_new_command(packet); +    g_gdb_packet_append(packet, "?"); + +    status = g_gdb_stream_send_packet(stream, packet); + +    if (!status) +        goto ggdgat_exit; + +    /* Réception de la réponse */ + +    packet = g_gdb_stream_recv_packet(stream); + +    g_gdb_packet_get_data(packet, &data, NULL, NULL); + +    start = strstr(data, "thread:"); +    if (start == NULL) goto ggdgat_exit; + +    start += sizeof("thread:") - 1 /* '\0' */; + +    end = strstr(start, ";"); +    if (end == NULL) goto ggdgat_exit; + +    result = strndup(start, end - start); + + ggdgat_exit: + +    g_gdb_stream_mark_packet_as_free(stream, packet); + +    return result; + +} + + + + + + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : stream = flux de communication ouvert avec le débogueur.     * +*                                                                             * +*  Description : Crée une définition des détails d'interfaçage GDB.           * +*                                                                             * +*  Retour      : Instance de détails mise en place ou NULL.                   * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +GGdbSupport *g_gdb_support_new(GGdbStream *stream) +{ +    GGdbSupport *result;                    /* Débogueur à retourner       */ +    GGdbPacket *packet;                     /* Paquet de communication GDB */ + + +    //goto end; + +    //goto skip; + +    packet = g_gdb_stream_get_free_packet(stream); + +    g_gdb_packet_start_new_command(packet); +    //g_gdb_packet_append(packet, "qSupported:multiprocess+;xmlRegisters"); +    g_gdb_packet_append(packet, "qSupported"); + +    g_gdb_packet_append(packet, "qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+"); + + +    bool test; + +    const char *data;                       /* Données reçues à analyser   */ +    size_t len; + +    test = g_gdb_stream_send_packet(stream, packet); + + + +    printf(" >> Paquet '%s' bien envoyé ? %s\n", "qSupported", test ? "oui" : "non"); + + + +    g_gdb_stream_mark_packet_as_free(stream, packet); + +    packet = g_gdb_stream_recv_packet(stream); + +    g_gdb_packet_get_data(packet, &data, &len, NULL); + +    printf(" << Réception de '%s'\n", data); + + + + +    result = g_object_new(G_TYPE_GDB_SUPPORT, NULL); + + + +    /* Découpage des éléments de réponse */ + +    char *answer;                           /* Réponse modifiable          */ +    char *save;                             /* Sauvegarde de position      */ +    char *token;                            /* Elément de réponse cerné    */ + +    answer = strdup(data); + +    for (token = strtok_r(answer, ";", &save); +         token != NULL; +         token = strtok_r(NULL, ";", &save)) +    { + + +        printf("TOKEN :: %s\n", token); + +        if (g_gdb_support_read_ulong(result, token, "PacketSize", &result->packet_size)) +            continue; + +        if (g_gdb_support_read_bool(result, token, "qXfer:osdata:read", &result->os_data)) +        { +            printf(" -->> %d\n", result->os_data); +            continue; +        } + + + + +    } + +    free(answer); + + + +    /** +     * Première chose : plus d'acquitement ! +     * +     * Dans les faits, c'est impossible à gérer en asynchrone. Par exemple : +     * +     *  C> vCont;c +     *  C> g           Txx... <S +     * +     * Si le client envoie une commande en même temps que le serveur envoie +     * quelque chose, le serveur attend dans tous les cas un acquitement. +     * Donc il va consommer les données envoyées par le client jusqu'à y +     * trouver ce qu'il cherche. +     */ + +    /* Envoi de la requête */ + +    packet = g_gdb_stream_get_free_packet(stream); + +    g_gdb_packet_start_new_command(packet); +    g_gdb_packet_append(packet, "QStartNoAckMode"); + +    test = g_gdb_stream_send_packet(stream, packet); + +    g_gdb_stream_mark_packet_as_free(stream, packet); + +    if (!test) +        goto ggsn_error; + +    /* Réception de la réponse */ + +    packet = g_gdb_stream_recv_packet(stream); + +    g_gdb_packet_get_data(packet, &data, NULL, NULL); + +    if (strcmp(data, "OK") != 0) +        goto ggsn_error; + +    g_gdb_stream_mark_packet_as_free(stream, packet); + +    /* Désactivation des acquitements */ + +    g_gdb_stream_do_not_ack(stream); + +    /** +     * Passage en mode étendu. C'est obligatoire pour pouvoir redémarrer un +     * programme débogué. +     */ + +    /* Envoi de la requête */ + +    packet = g_gdb_stream_get_free_packet(stream); + +    g_gdb_packet_start_new_command(packet); +    g_gdb_packet_append(packet, "!"); + +    test = g_gdb_stream_send_packet(stream, packet); + +    g_gdb_stream_mark_packet_as_free(stream, packet); + +    if (!test) +        goto ggsn_error; + +    /* Réception de la réponse */ + +    packet = g_gdb_stream_recv_packet(stream); + +    g_gdb_packet_get_data(packet, &data, NULL, NULL); + +    result->extended_mode = (strcmp(data, "OK") == 0); + +    g_gdb_stream_mark_packet_as_free(stream, packet); + + + + +    result->id = build_id(stream); + + + +#if 0 +    //end: + +#define CMD "?" + + +    packet = g_gdb_stream_get_free_packet(stream); + +    g_gdb_packet_start_new_command(packet); +    //g_gdb_packet_append(packet, "qSupported:multiprocess+;xmlRegisters"); +    g_gdb_packet_append(packet, CMD); + + +    test = g_gdb_stream_send_packet(stream, packet); + + + +    printf(" >> Paquet '%s' bien envoyé ? %s\n", CMD, test ? "oui" : "non"); + + + +    g_gdb_stream_mark_packet_as_free(stream, packet); + +    packet = g_gdb_stream_recv_packet(stream); + +    g_gdb_packet_get_data(packet, &data, &len, NULL); + +    printf(" << [pkt = %p ] Réception de '%s' (len=%d)\n", packet, data, (int)len); + + +#endif + + + +    // qfThreadInfo + + +#undef CMD + +    //#define CMD "qXfer:threads:read::0,1fff" +    //#define CMD "qXfer:btrace:read:all:0,1fff" +    //#define CMD "g" +    //#define CMD "m400000,8" +#define CMD "qsThreadInfo" + +    packet = g_gdb_stream_get_free_packet(stream); + +    g_gdb_packet_start_new_command(packet); +    //g_gdb_packet_append(packet, "qSupported:multiprocess+;xmlRegisters"); +    g_gdb_packet_append(packet, CMD); + + +    test = g_gdb_stream_send_packet(stream, packet); + + + +    printf(" >> Paquet '%s' bien envoyé ? %s\n", CMD, test ? "oui" : "non"); + + + +    g_gdb_stream_mark_packet_as_free(stream, packet); + +    packet = g_gdb_stream_recv_packet(stream); + +    g_gdb_packet_get_data(packet, &data, &len, NULL); + +    printf(" << [pkt = %p ] Réception de '%s' (len=%d)\n", packet, data, (int)len); + + + + + + + + + + + +    return result; + + ggsn_error: + + + +    return NULL; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : support = ensemble de détails à préciser.                    * +*                raw     = données brutes à parcourir.                        * +*                name    = désignation de la valeur recherchée.               * +*                value   = emplacement de la valeur à inscrire.               * +*                                                                             * +*  Description : Lit une valeur booléenne à partir des détails du serveur.    * +*                                                                             * +*  Retour      : true en cas d'affectation, false dans tous les autres cas.   * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_support_read_bool(GGdbSupport *support, const char *raw, const char *name, bool *value) +{ +    bool result;                            /* Bilan à retourner           */ +    size_t rlen;                            /* Taille de l'ensemble        */ +    size_t nlen;                            /* Taille du nom               */ + +    rlen = strlen(raw); +    nlen = strlen(name); + +    if ((nlen + 1) != rlen) +        return false; + +    if (strncmp(raw, name, nlen) != 0) +        return false; + +    switch (raw[nlen]) +    { +        case '+': +            *value = true; +            result = true; +            break; + +        case '-': +        case '?': +            *value = false; +            result = true; +            break; + +        default: +            result = false; +            break; + +    } + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : support = ensemble de détails à préciser.                    * +*                raw     = données brutes à parcourir.                        * +*                name    = désignation de la valeur recherchée.               * +*                value   = emplacement de la valeur à inscrire.               * +*                                                                             * +*  Description : Lit une valeur longue à partir des détails du serveur.       * +*                                                                             * +*  Retour      : true en cas d'affectation, false dans tous les autres cas.   * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_support_read_ulong(GGdbSupport *support, const char *raw, const char *name, unsigned long *value) +{ +    size_t rlen;                            /* Taille de l'ensemble        */ +    size_t nlen;                            /* Taille du nom               */ +    unsigned long v;                        /* Valeur récupérée à assigner */ + +    rlen = strlen(raw); +    nlen = strlen(name); + +    if (strncmp(raw, name, nlen) != 0) +        return false; + +    if (raw[nlen] != '=') +        return false; + +    v = strtoul(raw + nlen + 1, NULL, 16); + +    if (v == ULONG_MAX/* && errno == ERANGE*/) +        return false; + +    *value = v; + +    return true; + +} + + + + + + +char *g_gdb_support_get_id(const GGdbSupport *support) +{ +    return support->id; + +} + + + + + diff --git a/src/debug/gdbrsp/support.h b/src/debug/gdbrsp/support.h new file mode 100644 index 0000000..6b4ab63 --- /dev/null +++ b/src/debug/gdbrsp/support.h @@ -0,0 +1,73 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * support.h - prototypes pour la conformité dans l'interfaçage client/serveur + * + * Copyright (C) 2016 Cyrille Bagard + * + *  This file is part of Chrysalide. + * + *  Chrysalide is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 3 of the License, or + *  (at your option) any later version. + * + *  Chrysalide is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with Foobar.  If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _DEBUG_GDBRSP_SUPPORT_H +#define _DEBUG_GDBRSP_SUPPORT_H + + +#include <glib-object.h> +#include <stdbool.h> + + +#include "stream.h" + + + +#define G_TYPE_GDB_SUPPORT            (g_gdb_support_get_type()) +#define G_GDB_SUPPORT(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_GDB_SUPPORT, GGdbSupport)) +#define G_IS_GDB_SUPPORT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_GDB_SUPPORT)) +#define G_GDB_SUPPORT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_GDB_SUPPORT, GGdbSupportClass)) +#define G_IS_GDB_SUPPORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_GDB_SUPPORT)) +#define G_GDB_SUPPORT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_GDB_SUPPORT, GGdbSupportClass)) + + +/* Indications quant à l'interfaçage client/serveur GDB (instance) */ +typedef struct _GGdbSupport GGdbSupport; + +/* Indications quant à l'interfaçage client/serveur GDB (classe) */ +typedef struct _GGdbSupportClass GGdbSupportClass; + + +/* Indique le type défini par la GLib pour les détails d'interfaçage GDB. */ +GType g_gdb_support_get_type(void); + +/* Crée une définition des détails d'interfaçage GDB. */ +GGdbSupport *g_gdb_support_new(GGdbStream *); + + + + + + + +char *g_gdb_support_get_id(const GGdbSupport *support); + + + + + + + + + +#endif  /* _DEBUG_GDBRSP_SUPPORT_H */ diff --git a/src/debug/gdbrsp/target.c b/src/debug/gdbrsp/target.c new file mode 100644 index 0000000..d6edf6c --- /dev/null +++ b/src/debug/gdbrsp/target.c @@ -0,0 +1,950 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * target.c - gestion des éléments propres à l'architecture reconnue par GDB + * + * Copyright (C) 2016 Cyrille Bagard + * + *  This file is part of Chrysalide. + * + *  Chrysalide is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 3 of the License, or + *  (at your option) any later version. + * + *  Chrysalide is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with Foobar.  If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "target.h" + + +#include <assert.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + + +#include "utils.h" +#include "../../common/cpp.h" +#include "../../common/extstr.h" +#include "../../common/xml.h" + + + +/* Définitions de registres */ + +typedef struct _arch_register_t +{ +    char *name;                             /* Nom de registre             */ +    unsigned int size;                      /* Taille en bits              */ + +} arch_register_t; + +typedef struct _target_cpu_t +{ +    char *label;                            /* Désignation de l'ensemble   */ + +    arch_register_t *regs;                  /* Définition des registres    */ +    unsigned int count;                     /* Quantité de ces définitions */ + +} target_cpu_t; + + +/* Indications quant à l'interfaçage client/serveur GDB (instance) */ +struct _GGdbTarget +{ +    GObject parent;                         /* A laisser en premier        */ + +    target_cpu_t **defs;                    /* Liste de définitions        */ +    size_t count;                           /* Taille de cette même liste  */ + +    bool read_single_register;              /* Lecture spécifique permise ?*/ +    bool write_single_register;             /* Ecriture spécifique valide ?*/ + +}; + +/* Indications quant à l'interfaçage client/serveur GDB (classe) */ +struct _GGdbTargetClass +{ +    GObjectClass parent;                    /* A laisser en premier        */ + +}; + + +/* Initialise la classe des détails d'interfaçage GDB. */ +static void g_gdb_target_class_init(GGdbTargetClass *); + +/* Procède à l'initialisation des détails d'interfaçage GDB. */ +static void g_gdb_target_init(GGdbTarget *); + +/* Supprime toutes les références externes. */ +static void g_gdb_target_dispose(GGdbTarget *); + +/* Procède à la libération totale de la mémoire. */ +static void g_gdb_target_finalize(GGdbTarget *); + +/* Charge la définition d'un groupe de registres. */ +static bool g_gdb_target_load_register_definition(GGdbTarget *, GGdbStream *, const char *); + +/* Recherche l'indice correspondant à un registre donné. */ +static bool g_gdb_target_find_register_index(const GGdbTarget *, const char *, unsigned int *); + +/* Recherche la position correspondant à un registre donné. */ +static bool g_gdb_target_find_register_offset(const GGdbTarget *, unsigned int, size_t *); + + + +/* Indique le type défini par la GLib pour les détails d'interfaçage GDB. */ +G_DEFINE_TYPE(GGdbTarget, g_gdb_target, G_TYPE_OBJECT); + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : klass = classe de débogueur à initialiser.                   * +*                                                                             * +*  Description : Initialise la classe des détails d'interfaçage GDB.          * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void g_gdb_target_class_init(GGdbTargetClass *klass) +{ +    GObjectClass *object;                   /* Autre version de la classe  */ + +    object = G_OBJECT_CLASS(klass); + +    object->dispose = (GObjectFinalizeFunc/* ! */)g_gdb_target_dispose; +    object->finalize = (GObjectFinalizeFunc)g_gdb_target_finalize; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : target = instance de débogueur à préparer.                   * +*                                                                             * +*  Description : Procède à l'initialisation des détails d'interfaçage GDB.    * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void g_gdb_target_init(GGdbTarget *target) +{ +    target->defs = NULL; +    target->count = 0; + +    target->read_single_register = true; +    target->write_single_register = true; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : target = instance d'objet GLib à traiter.                    * +*                                                                             * +*  Description : Supprime toutes les références externes.                     * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void g_gdb_target_dispose(GGdbTarget *target) +{ +    G_OBJECT_CLASS(g_gdb_target_parent_class)->dispose(G_OBJECT(target)); + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : target = instance d'objet GLib à traiter.                    * +*                                                                             * +*  Description : Procède à la libération totale de la mémoire.                * +*                                                                             * +*  Retour      : -                                                            * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static void g_gdb_target_finalize(GGdbTarget *target) +{ +    G_OBJECT_CLASS(g_gdb_target_parent_class)->finalize(G_OBJECT(target)); + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : stream = flux de communication ouvert avec le débogueur.     * +*                                                                             * +*  Description : Crée une définition des détails d'interfaçage GDB.           * +*                                                                             * +*  Retour      : Instance de détails mise en place ou NULL.                   * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +GGdbTarget *g_gdb_target_new(GGdbStream *stream) +{ +    GGdbTarget *result;                     /* Débogueur à retourner       */ +    GGdbPacket *packet;                     /* Paquet de communication GDB */ +    bool status;                            /* Bilan d'une communication   */ + +    const char *data;                       /* Données reçues du serveur   */ +    size_t len;                             /* Quantité de ces données     */ +    char *xmldata;                          /* Données modifiables         */ +    xmlDocPtr xdoc;                         /* Document XML récupéré       */ +    xmlXPathContextPtr context;             /* COntexte d'analyse associé  */ +    xmlXPathObjectPtr xobject;              /* Cible d'une recherche       */ +    unsigned int i;                         /* Boucle de parcours          */ +    char *access;                           /* Chemin d'accès à un élément */ +    char *xmlref;                           /* Référence de définitions    */ + + + + +    result = NULL; + + +    //goto end; + +    //goto skip; + + +    packet = g_gdb_stream_get_free_packet(stream); + +    g_gdb_packet_start_new_command(packet); +    //g_gdb_packet_append(packet, "qTargeted:multiprocess+;xmlRegisters"); +    g_gdb_packet_append(packet, "qXfer:features:read:target.xml:0,3fff"); + +    //g_gdb_packet_append(packet, "qTargeted:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContTargeted+;QThreadEvents+;no-resumed+"); + + + +    status = g_gdb_stream_send_packet(stream, packet); +    if (!status) goto ggtn_failed; + + + + +    g_gdb_stream_mark_packet_as_free(stream, packet); + +    packet = g_gdb_stream_recv_packet(stream); + +    g_gdb_packet_get_data(packet, &data, &len, NULL); + +    printf(" << Réception de '%s'\n", data); + +    /* Marqueur de fin placé au début ?! */ +    if (data[0] != 'l') +        goto ggtn_failed; + +    xmldata = strdup(data + 1); + +    /** +     * On cherche à éviter la déconvenue suivante avec la libxml2 : +     * +     *    noname.xml:12: namespace error : Namespace prefix xi on include is not defined +     *      <xi:include href="aarch64-core.xml"/> +     */ + +    xmldata = strrpl(xmldata, "xi:include", "include"); + +    if (!load_xml_from_memory(xmldata, len - 1, &xdoc, &context)) +        goto ggtn_failed; + + +    result = g_object_new(G_TYPE_GDB_TARGET, NULL); + + +    xobject = get_node_xpath_object(context, "/target/include"); + +    for (i = 0; i < XPATH_OBJ_NODES_COUNT(xobject); i++) +    { +        asprintf(&access, "/target/include[position()=%u]", i + 1); + +        xmlref = get_node_prop_value(context, access, "href"); + +        free(access); + +        if (xmlref != NULL) +        { +            printf("REF>> %s\n", xmlref); +            /*static bool */g_gdb_target_load_register_definition(result, stream, xmlref); + +            free(xmlref); + +        } + +    } + +    if(xobject != NULL) +        xmlXPathFreeObject(xobject); + +    close_xml_file(xdoc, context); + +    free(xmldata); + + + + + + + + + + +    //result = g_object_new(G_TYPE_GDB_TARGET, NULL); + + + ggtn_failed: + +    g_gdb_stream_mark_packet_as_free(stream, packet); + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : target = ensemble d'informations liées à l'architecture.     * +*                stream = flux de communication ouvert avec le débogueur.     * +*                name   = désignation des définitions de registres à charger. * +*                                                                             * +*  Description : Charge la définition d'un groupe de registres.               * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_target_load_register_definition(GGdbTarget *target, GGdbStream *stream, const char *name) +{ +    bool result;                            /* Bilan à retourner           */ +    GGdbPacket *packet;                     /* Paquet de communication GDB */ +    bool status;                            /* Bilan d'une communication   */ +    const char *data;                       /* Données reçues du serveur   */ +    size_t len;                             /* Quantité de ces données     */ +    xmlDocPtr xdoc;                         /* Document XML récupéré       */ +    xmlXPathContextPtr context;             /* COntexte d'analyse associé  */ +    xmlXPathObjectPtr xobject;              /* Cible d'une recherche       */ +    target_cpu_t *def;                      /* Nouvelle définition à lire  */ +    unsigned int i;                         /* Boucle de parcours          */ +    char *access;                           /* Chemin d'accès à un élément */ +    char *type;                             /* Espèce de définition        */ + +    result = false; + +    /* Envoi de la requête */ + +    packet = g_gdb_stream_get_free_packet(stream); + +    g_gdb_packet_start_new_command(packet); + +    g_gdb_packet_append(packet, "qXfer:features:read:"); +    g_gdb_packet_append(packet, name); +    g_gdb_packet_append(packet, ":0,3fff"); + +    status = g_gdb_stream_send_packet(stream, packet); +    if (!status) goto ggtlrd_failed; + +    g_gdb_stream_mark_packet_as_free(stream, packet); + +    /* Réception de la réponse */ + +    packet = g_gdb_stream_recv_packet(stream); + +    g_gdb_packet_get_data(packet, &data, &len, NULL); + +    //printf(">>>> '%s'\n", data); + +    /* Marqueur de fin placé au début ?! */ +    if (data[0] != 'l') +        goto ggtlrd_failed; + +    if (!load_xml_from_memory(data + 1, len - 1, &xdoc, &context)) +        goto ggtlrd_failed; + +    /* Chargement des définitions */ + +    xobject = get_node_xpath_object(context, "/feature/*"); + +    def = (target_cpu_t *)calloc(1, sizeof(target_cpu_t)); + +    def->count = XPATH_OBJ_NODES_COUNT(xobject); +    def->regs = (arch_register_t *)calloc(def->count, sizeof(arch_register_t)); + +    for (i = 0; i < XPATH_OBJ_NODES_COUNT(xobject); i++) +    { +        asprintf(&access, "/feature/*[position()=%u]", i + 1); + +        type = get_node_name(context, access); + +        if (strcmp(type, "reg") == 0) +        { +            def->regs[i].name = get_node_prop_value(context, access, "name"); +            def->regs[i].size = atoi(get_node_prop_value(context, access, "bitsize")); + +            //printf("load reg '%s' (%u)\n", def->regs[i].name, def->regs[i].size); + +        } + +        free(type); + +        free(access); + +    } + +    if(xobject != NULL) +        xmlXPathFreeObject(xobject); + +    close_xml_file(xdoc, context); + +    /* Intégration finale */ + +    target->defs = (target_cpu_t **)realloc(target->defs, ++target->count * sizeof(target_cpu_t *)); + +    target->defs[target->count - 1] = def; + + ggtlrd_failed: + +    g_gdb_stream_mark_packet_as_free(stream, packet); + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : target = ensemble d'informations liées à l'architecture.     * +*                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_gdb_target_get_register_names(const GGdbTarget *target, const char *group, size_t *count) +{ +    char **result;                          /* Désignations à retourner    */ +    unsigned int i;                         /* Boucle de parcours #1       */ +    const target_cpu_t *rgrp;               /* Groupe de registres         */ +    unsigned int j;                         /* Boucle de parcours #2       */ + +    result = NULL; + +    for (i = 0; i < target->count && result == NULL; i++) +    { +        rgrp = target->defs[i]; + +        if (group != NULL) +        { +            if (strcmp(rgrp->label, group) != 0) +                continue; +        } + +        *count = rgrp->count; + +        result = (char **)calloc(*count, sizeof(char *)); + +        for (j = 0; j < *count; j++) +            result[j] = strdup(rgrp->regs[j].name); + +    } + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : target = ensemble d'informations liées à l'architecture.     * +*                name   = désignation du registre visé.                       * +*                                                                             * +*  Description : Indique la taille associée à un registre donné.              * +*                                                                             * +*  Retour      : Taille en bits, ou 0 si le registre n'a pas été trouvé.      * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +unsigned int g_gdb_target_get_register_size(const GGdbTarget *target, const char *name) +{ +    unsigned int result;                    /* Taille en bits  à retourner */ +    unsigned int i;                         /* Boucle de parcours #1       */ +    const target_cpu_t *rgrp;               /* Groupe de registres         */ +    unsigned int j;                         /* Boucle de parcours #2       */ + +    result = 0; + +    for (i = 0; i < target->count && result == 0; i++) +    { +        rgrp = target->defs[i]; + +        for (j = 0; j < rgrp->count; j++) +            if (strcmp(rgrp->regs[j].name, name) == 0) +                result = rgrp->regs[j].size; + +    } + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : target = ensemble d'informations liées à l'architecture.     * +*                reg    = désignation humaine du register à consulter.        * +*                index  = indice correspondant au registre pour GDB. [OUT]    * +*                                                                             * +*  Description : Recherche l'indice correspondant à un registre donné.        * +*                                                                             * +*  Retour      : Bilan de l'opération : trouvaille ou échec ?                 * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_target_find_register_index(const GGdbTarget *target, const char *reg, unsigned int *index) +{ +    bool result;                            /* Bilan à retourner           */ +    unsigned int i;                         /* Boucle de parcours #1       */ +    unsigned int j;                         /* Boucle de parcours #2       */ + +    result = false; + +    *index = 0; + +    for (i = 0; i < target->count && !result; i++) +        for (j = 0; j < target->defs[i]->count && !result; j++) +        { +            if (strcmp(target->defs[i]->regs[j].name, reg) == 0) +                result = true; +            else +                (*index)++; +        } + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : target = ensemble d'informations liées à l'architecture.     * +*                index  = indice correspondant au registre pour GDB.          * +*                offset = position de valeur du registre dans du texte. [OUT] * +*                                                                             * +*  Description : Recherche la position correspondant à un registre donné.     * +*                                                                             * +*  Retour      : Bilan de l'opération : trouvaille ou échec ?                 * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +static bool g_gdb_target_find_register_offset(const GGdbTarget *target, unsigned int index, size_t *offset) +{ +    unsigned int i;                         /* Boucle de parcours #1       */ +    unsigned int j;                         /* Boucle de parcours #2       */ + +    *offset = 0; + +    for (i = 0; i < target->count && index > 0; i++) +        for (j = 0; j < target->defs[i]->count && index > 0; j++) +        { +            assert(target->defs[i]->regs[j].size % 4 == 0); + +            *offset += target->defs[i]->regs[j].size / 4; + +            index--; + +        } + +    return (index == 0); + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : target = ensemble d'informations liées à l'architecture.     * +*                stream = flux de communication ouvert avec le débogueur.     * +*                endian = boutisme de la cible.                               * +*                reg    = désignation humaine du register à consulter.        * +*                size   = taille des données mises en jeu.                    * +*                ...    = emplacement de la valeur lue à conserver. [OUT]     * +*                                                                             * +*  Description : Effectue la lecture d'un registre donné.                     * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +bool g_gdb_target_read_register(GGdbTarget *target, GGdbStream *stream, SourceEndian endian, const char *reg, size_t size, ...) +{ +    bool result;                            /* Bilan à retourner           */ +    unsigned int index;                     /* Indice du registre ciblé    */ +    GGdbPacket *packet;                     /* Paquet de communication     */ +    char cmd[sizeof(XSTR(UINT_MAX)) + 1];   /* Elément de requête          */ +    const char *data;                       /* Données reçues à analyser   */ +    size_t len;                             /* Quantité de ces données     */ +    const char *raw;                        /* Début de zone à relire      */ +    size_t offset;                          /* Position dans la masse      */ +    va_list ap;                             /* Liste variable d'arguments  */ +    uint8_t *val8;                          /* Valeur sur 8 bits           */ +    uint16_t *val16;                        /* Valeur sur 16 bits          */ +    uint16_t conv16;                        /* Valeur adaptée sur 16 bits  */ +    uint32_t *val32;                        /* Valeur sur 32 bits          */ +    uint32_t conv32;                        /* Valeur adaptée sur 32 bits  */ +    uint64_t *val64;                        /* Valeur sur 64 bits          */ +    uint64_t conv64;                        /* Valeur adaptée sur 64 bits  */ + +    result = g_gdb_target_find_register_index(target, reg, &index); +    if (!result) goto ggtrr_error; + +    /** +     * Essai avec la méthode précise. +     */ + +    if (!target->read_single_register) +        goto read_all_register_fallback; + +    packet = g_gdb_stream_get_free_packet(stream); + +    g_gdb_packet_start_new_command(packet); +    g_gdb_packet_append(packet, "p"); + +    snprintf(cmd, sizeof(cmd), "%x", index); +    g_gdb_packet_append(packet, cmd); + +    result = g_gdb_stream_send_packet(stream, packet); + +    g_gdb_stream_mark_packet_as_free(stream, packet); + +    if (!result) +        goto ggtrr_error; + +    /* Réception de la réponse */ + +    packet = g_gdb_stream_recv_packet(stream); + +    g_gdb_packet_get_data(packet, &data, &len, NULL); + +    if (len != 0 && !is_error_code(data, len)) +        raw = data; + +    else +    { +        target->read_single_register = false; + +        g_gdb_stream_mark_packet_as_free(stream, packet); + + read_all_register_fallback: + +        /** +         * Utilisation de la méthode de masse au besoin... +         */ + +        packet = g_gdb_stream_get_free_packet(stream); + +        g_gdb_packet_start_new_command(packet); +        g_gdb_packet_append(packet, "g"); + +        result = g_gdb_stream_send_packet(stream, packet); + +        g_gdb_stream_mark_packet_as_free(stream, packet); + +        if (!result) +            goto ggtrr_error; + +        /* Réception de la réponse */ + +        packet = g_gdb_stream_recv_packet(stream); + +        g_gdb_packet_get_data(packet, &data, &len, NULL); + +        result = g_gdb_target_find_register_offset(target, index, &offset); + +        if (!result || offset > len) +            goto ggtrr_exit; + +        raw = data + offset; +        len -= offset; + +    } + +    /* Lecture finale de la valeur recherchée */ + +    va_start(ap, size); + +    switch (size) +    { +        case 8: +            val8 = va_arg(ap, uint8_t *); +            result = hex_to_u8(raw, val8); +            break; + +        case 16: +            val16 = va_arg(ap, uint16_t *); +            result = hex_to_u16(raw, &conv16); +            *val16 = from_u16(&conv16, endian); +            break; + +        case 32: +            val32 = va_arg(ap, uint32_t *); +            result = hex_to_u32(raw, &conv32); +            *val32 = from_u32(&conv32, endian); +            break; + +        case 64: +            val64 = va_arg(ap, uint64_t *); +            result = hex_to_u64(raw, &conv64); +            *val64 = from_u64(&conv64, endian); +            break; + +        default: +            assert(false); +            result = false; +            break; + +    } + +    va_end(ap); + + ggtrr_exit: + +    g_gdb_stream_mark_packet_as_free(stream, packet); + + ggtrr_error: + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : target = ensemble d'informations liées à l'architecture.     * +*                stream = flux de communication ouvert avec le débogueur.     * +*                endian = boutisme de la cible.                               * +*                reg    = désignation humaine du register à consulter.        * +*                size   = taille des données mises en jeu.                    * +*                ...    = emplacement de la valeur à écrire.                  * +*                                                                             * +*  Description : Effectue l'écriture d'un registre donné.                     * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +bool g_gdb_target_write_register(GGdbTarget *target, GGdbStream *stream, SourceEndian endian, const char *reg, size_t size, ...) +{ +    bool result;                            /* Bilan d'opération à renvoyer*/ +    va_list ap;                             /* Liste variable d'arguments  */ +    const uint8_t *val8;                    /* Valeur sur 8 bits           */ +    const uint16_t *val16;                  /* Valeur sur 16 bits          */ +    uint16_t conv16;                        /* Valeur adaptée sur 16 bits  */ +    const uint32_t *val32;                  /* Valeur sur 32 bits          */ +    uint32_t conv32;                        /* Valeur adaptée sur 32 bits  */ +    const uint64_t *val64;                  /* Valeur sur 64 bits          */ +    uint64_t conv64;                        /* Valeur adaptée sur 64 bits  */ +    char hexval[17];                        /* Valeur sous forme hexa      */ +    unsigned int index;                     /* Indice du registre ciblé    */ +    GGdbPacket *packet;                     /* Paquet de communication     */ +    char cmd[sizeof(XSTR(UINT_MAX)) + 1];   /* Elément de requête          */ +    const char *data;                       /* Données reçues à analyser   */ +    size_t len;                             /* Quantité de ces données     */ +    char *new;                              /* Nouvelles valeurs générales */ +    size_t offset;                          /* Position dans la masse      */ + +    /* Tronc commun : récupération de la valeur */ + +    va_start(ap, size); + +    switch (size) +    { +        case 8: +            val8 = va_arg(ap, uint8_t *); +            result = u8_to_hex(val8, hexval); +            break; + +        case 16: +            val16 = va_arg(ap, uint16_t *); +            conv16 = to_u16(val16, endian); +            result = u16_to_hex(&conv16, hexval); +            break; + +        case 32: +            val32 = va_arg(ap, uint32_t *); +            conv32 = to_u32(val32, endian); +            result = u32_to_hex(&conv32, hexval); +            break; + +        case 64: +            val64 = va_arg(ap, uint64_t *); +            conv64 = to_u64(val64, endian); +            result = u16_to_hex(&conv64, hexval); +            break; + +        default: +            assert(false); +            result = false; +            break; + +    } + +    va_end(ap); + +    if (!result) +        goto ggtwr_error; + +    /* Préparation de la suite */ + +    result = g_gdb_target_find_register_index(target, reg, &index); +    if (!result) goto ggtwr_error; + +    /** +     * Essai avec la méthode précise. +     */ + +    if (!target->write_single_register) +        goto write_all_register_fallback; + +    packet = g_gdb_stream_get_free_packet(stream); + +    g_gdb_packet_start_new_command(packet); +    g_gdb_packet_append(packet, "P"); + +    snprintf(cmd, sizeof(cmd), "%x", index); +    g_gdb_packet_append(packet, cmd); + +    g_gdb_packet_append(packet, "="); + +    g_gdb_packet_append(packet, hexval); + +    result = g_gdb_stream_send_packet(stream, packet); + +    g_gdb_stream_mark_packet_as_free(stream, packet); + +    if (!result) +        goto ggtwr_error; + +    /* Réception de la réponse */ + +    packet = g_gdb_stream_recv_packet(stream); + +    g_gdb_packet_get_data(packet, &data, &len, NULL); + +    if (is_error_code(data, len) || strcmp(data, "OK") != 0) +    { +        target->write_single_register = false; + +        g_gdb_stream_mark_packet_as_free(stream, packet); + + write_all_register_fallback: + +        /** +         * Utilisation de la méthode de masse au besoin... +         */ + +        /* Lecture de l'ensemble des registres */ + +        packet = g_gdb_stream_get_free_packet(stream); + +        g_gdb_packet_start_new_command(packet); +        g_gdb_packet_append(packet, "g"); + +        result = g_gdb_stream_send_packet(stream, packet); + +        g_gdb_stream_mark_packet_as_free(stream, packet); + +        if (!result) +            goto ggtwr_error; + +        /* Réception de la réponse et mise à jour */ + +        packet = g_gdb_stream_recv_packet(stream); + +        g_gdb_packet_get_data(packet, &data, &len, NULL); + +        result = g_gdb_target_find_register_offset(target, index, &offset); + +        if (!result || offset > len) +            goto ggtwr_exit; + +        new = (char *)malloc(len); + +        memcpy(new, data, len); +        memcpy(new + offset, hexval, strlen(hexval)); + +        g_gdb_stream_mark_packet_as_free(stream, packet); + +        /* Ecrasement de tous les registres */ + +        packet = g_gdb_stream_get_free_packet(stream); + +        g_gdb_packet_start_new_command(packet); +        g_gdb_packet_append(packet, "G"); + +        g_gdb_packet_append(packet, new); +        free(new); + +        result = g_gdb_stream_send_packet(stream, packet); + +        g_gdb_stream_mark_packet_as_free(stream, packet); + +        if (!result) +            goto ggtwr_error; + +        /* Réception de la réponse */ + +        packet = g_gdb_stream_recv_packet(stream); + +        g_gdb_packet_get_data(packet, &data, &len, NULL); + +        result = (!is_error_code(data, len) && strcmp(data, "OK") == 0); + +    } + + ggtwr_exit: + +    g_gdb_stream_mark_packet_as_free(stream, packet); + + ggtwr_error: + +    return result; + +} diff --git a/src/debug/gdbrsp/target.h b/src/debug/gdbrsp/target.h new file mode 100644 index 0000000..805ff49 --- /dev/null +++ b/src/debug/gdbrsp/target.h @@ -0,0 +1,72 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * target.h - prototypes pour la gestion des éléments propres à l'architecture reconnue par GDB + * + * Copyright (C) 2016 Cyrille Bagard + * + *  This file is part of Chrysalide. + * + *  Chrysalide is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 3 of the License, or + *  (at your option) any later version. + * + *  Chrysalide is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with Foobar.  If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _DEBUG_GDBRSP_TARGET_H +#define _DEBUG_GDBRSP_TARGET_H + + +#include <glib-object.h> +#include <stdbool.h> + + +#include "stream.h" +#include "../../common/endianness.h" + + + +#define G_TYPE_GDB_TARGET            (g_gdb_target_get_type()) +#define G_GDB_TARGET(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_GDB_TARGET, GGdbTarget)) +#define G_IS_GDB_TARGET(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_GDB_TARGET)) +#define G_GDB_TARGET_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_GDB_TARGET, GGdbTargetClass)) +#define G_IS_GDB_TARGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_GDB_TARGET)) +#define G_GDB_TARGET_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_GDB_TARGET, GGdbTargetClass)) + + +/* Indications quant à l'interfaçage client/serveur GDB (instance) */ +typedef struct _GGdbTarget GGdbTarget; + +/* Indications quant à l'interfaçage client/serveur GDB (classe) */ +typedef struct _GGdbTargetClass GGdbTargetClass; + + +/* Indique le type défini par la GLib pour les détails d'interfaçage GDB. */ +GType g_gdb_target_get_type(void); + +/* Crée une définition des détails d'interfaçage GDB. */ +GGdbTarget *g_gdb_target_new(GGdbStream *); + +/* Liste l'ensemble des registres appartenant à un groupe. */ +char **g_gdb_target_get_register_names(const GGdbTarget *, const char *, size_t *); + +/* Indique la taille associée à un registre donné. */ +unsigned int g_gdb_target_get_register_size(const GGdbTarget *, const char *); + +/* Effectue la lecture d'un registre donné. */ +bool g_gdb_target_read_register(GGdbTarget *, GGdbStream *, SourceEndian, const char *, size_t, ...); + +/* Effectue l'écriture d'un registre donné. */ +bool g_gdb_target_write_register(GGdbTarget *, GGdbStream *, SourceEndian, const char *, size_t, ...); + + + +#endif  /* _DEBUG_GDBRSP_TARGET_H */ diff --git a/src/debug/remgdb/tcp.c b/src/debug/gdbrsp/tcp.c index aa10c54..d6a1ef8 100644 --- a/src/debug/remgdb/tcp.c +++ b/src/debug/gdbrsp/tcp.c @@ -137,7 +137,7 @@ static int connect_via_tcp(const char *server, const char *port)      struct addrinfo *infos;                 /* Informations disponibles    */      int ret;                                /* Bilan d'un appel            */      struct addrinfo *iter;                  /* Boucle de parcours          */ -    struct sockaddr_in addr;                /* Infos de connexion distante */           +    struct sockaddr_in addr;                /* Infos de connexion distante */      memset(&hints, 0, sizeof(struct addrinfo)); @@ -189,6 +189,7 @@ static int connect_via_tcp(const char *server, const char *port)  *                                                                             *  *  Paramètres  : server = nom ou adresse du serveur à contacter.              *  *                port   = port de connexion.                                  * +*                owner  = débogueur tributaire du canal de communication.     *  *                                                                             *  *  Description : Crée une nouvelle connexion TCP à un serveur GDB.            *  *                                                                             * @@ -198,7 +199,7 @@ static int connect_via_tcp(const char *server, const char *port)  *                                                                             *  ******************************************************************************/ -GGdbStream *g_gdb_tcp_client_new(const char *server, const char *port) +GGdbStream *g_gdb_tcp_client_new(const char *server, const char *port, GGdbDebugger *owner)  {      GGdbTcpClient *result;                  /* Structure à retourner       */      int sock;                               /* Flux ouvert à construire    */ @@ -210,6 +211,9 @@ GGdbStream *g_gdb_tcp_client_new(const char *server, const char *port)      G_GDB_STREAM(result)->fd = sock; +    G_GDB_STREAM(result)->owner = owner; +    g_object_ref(G_OBJECT(owner)); +      if (!g_gdb_stream_listen(G_GDB_STREAM(result)))          goto ggtcn_error; @@ -238,7 +242,14 @@ GGdbStream *g_gdb_tcp_client_new(const char *server, const char *port)  static bool g_gdb_tcp_client_send_data(GGdbTcpClient *client, const char *data, size_t len)  { -    return (send(G_GDB_STREAM(client)->fd, data, len, 0) == len); +    ssize_t sent;                           /* Quantité de données envoyée */ + +    sent = send(G_GDB_STREAM(client)->fd, data, len, 0); + +    //printf("  sent '%s'\n", data); +    //printf("  sent ? %d vs %d\n", (int)sent, (int)len); + +    return (sent == len);  } @@ -258,6 +269,12 @@ static bool g_gdb_tcp_client_send_data(GGdbTcpClient *client, const char *data,  static bool g_gdb_tcp_client_recv_byte(GGdbTcpClient *client, char *data)  { -    return (recv(G_GDB_STREAM(client)->fd, data, 1, 0) == 1); +    ssize_t got;                            /* Quantité de données reçue   */ + +    got = recv(G_GDB_STREAM(client)->fd, data, 1, 0); + +    //printf("  got ? %d vs %d -> %c (0x%02hhx\n", (int)got, (int)1, *data, *data); + +    return (got == 1);  } diff --git a/src/debug/remgdb/tcp.h b/src/debug/gdbrsp/tcp.h index 7d40f16..18d6c33 100644 --- a/src/debug/remgdb/tcp.h +++ b/src/debug/gdbrsp/tcp.h @@ -21,10 +21,11 @@   */ -#ifndef _DEBUG_REMGDB_TCP_H -#define _DEBUG_REMGDB_TCP_H +#ifndef _DEBUG_GDBRSP_TCP_H +#define _DEBUG_GDBRSP_TCP_H +#include "gdb.h"  #include "stream.h" @@ -49,8 +50,8 @@ typedef struct _GGdbTcpClientClass GGdbTcpClientClass;  GType g_gdb_tcp_client_get_type(void);  /* Crée une nouvelle connexion TCP à un serveur GDB. */ -GGdbStream *g_gdb_tcp_client_new(const char *, const char *); +GGdbStream *g_gdb_tcp_client_new(const char *, const char *, GGdbDebugger *); -#endif  /* _DEBUG_REMGDB_TCP_H */ +#endif  /* _DEBUG_GDBRSP_TCP_H */ diff --git a/src/debug/gdbrsp/utils.c b/src/debug/gdbrsp/utils.c new file mode 100644 index 0000000..8c4cb8a --- /dev/null +++ b/src/debug/gdbrsp/utils.c @@ -0,0 +1,351 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * utils.h - fonctions qui simplifient la vie dans les interactions avec un serveur GDB + * + * Copyright (C) 2016 Cyrille Bagard + * + *  This file is part of Chrysalide. + * + *  Chrysalide is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 3 of the License, or + *  (at your option) any later version. + * + *  Chrysalide is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with Foobar.  If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "utils.h" + + +#include <assert.h> +#include <ctype.h> +#include <stdarg.h> +#include <stdbool.h> +#include <sys/param.h> +#include <sys/types.h> + + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : data = données à inspecter.                                  * +*                len  = quantité de ces données.                              * +*                                                                             * +*  Description : Indique si les données correspondent à un code d'erreur.     * +*                                                                             * +*  Retour      : Bilan de l'analyse.                                          * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +bool is_error_code(const char *data, size_t len) +{ +    bool result;                            /* Bilan à retourner           */ + +    result = (len == 3); + +    if (result) +        result = (data[0] == 'E' && isdigit(data[1]) && isdigit(data[2])); + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : data = données à analyser.                                   * +*                size = taille de ces données.                                * +*                byte = statut de sortie d'un programme. [OUT]                * +*                                                                             * +*  Description : Relit une valeur sur 8 bits et deux lettres.                 * +*                                                                             * +*  Retour      : Bilan de la lecture.                                         * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +bool read_fixed_byte(const char *data, size_t len, uint8_t *byte) +{ +    bool result;                            /* Bilan à retourner           */ +    const char *iter;                       /* Boucle de parcours #1       */ +    size_t i;                               /* Boucle de parcours #2       */ +    uint8_t nibble;                         /* Valeur affichée             */ + +    result = true; + +    len = MIN(2, len); + +    for (i = 0, iter = data; i < len && result; i++, iter++) +    { +        switch (*iter) +        { +            case '0' ... '9': +                nibble = *iter - '0'; +                break; + +            case 'a' ... 'f': +                nibble = *iter - 'a' + 10; +                break; + +            case 'A' ... 'F': +                nibble = *iter - 'A' + 10; +                break; + +            default: +                result = false; +                break; + +        } + +        if (i == 0) +            *byte = (nibble << 4); +        else +            *byte |= nibble; + +    } + +    if (result) +        result = (i == 2); + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : hex   = tampon d'origine assez grand.                        * +*                size  = taille de la valeur à considérer pour les travaux.   * +*                value = valeur sur XX bits à transcrire. [OUT]               * +*                                                                             * +*  Description : Traduit en valeur sur XX bits une forme textuelle.           * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +bool hex_to_any_u(const char *hex, size_t size, ...) +{ +    bool result;                            /* Bilan à retourner           */ +    va_list ap;                             /* Gestion de l'inconnue       */ +    uint8_t *value8;                        /* Valeur sur 8 bits           */ +    uint16_t *value16;                      /* Valeur sur 16 bits          */ +    uint32_t *value32;                      /* Valeur sur 32 bits          */ +    uint64_t *value64;                      /* Valeur sur 64 bits          */ +    uint8_t *iter;                          /* Boucle de parcours #1       */ +    size_t i;                               /* Boucle de parcours #2       */ +    char nibble;                            /* Valeur à afficher           */ + +    result = false; + +    /* Récupération de la destination */ + +    va_start(ap, size); + +    switch (size) +    { +        case 1: +            value8 = va_arg(ap, uint8_t *); +            iter = value8; +            break; + +        case 2: +            value16 = va_arg(ap, uint16_t *); +            iter = (uint8_t *)value16; +            break; + +        case 4: +            value32 = va_arg(ap, uint32_t *); +            iter = (uint8_t *)value32; +            break; + +        case 8: +            value64 = va_arg(ap, uint64_t *); +            iter = (uint8_t *)value64; +            break; + +        default: +            goto done; +            break; + +    } + +    /* Lecture de la valeur */ + +    for (i = 0; i < size; i++, iter++) +    { +        *iter = 0; + +        nibble = hex[i * 2]; + +        switch (nibble) +        { +            case '0' ... '9': +                *iter = (nibble - '0') << 4; +                break; + +            case 'a' ... 'f': +                *iter = (nibble - 'a' + 0xa) << 4; +                break; + +            case 'A' ... 'F': +                *iter = (nibble - 'A' + 0xa) << 4; +                break; + +            default: +                goto done; +                break; + +        } + +        nibble = hex[i * 2 + 1]; + +        switch (nibble) +        { +            case '0' ... '9': +                *iter |= (nibble - '0'); +                break; + +            case 'a' ... 'f': +                *iter |= (nibble - 'a' + 0xa); +                break; + +            case 'A' ... 'F': +                *iter |= (nibble - 'A' + 0xa); +                break; + +            default: +                goto done; +                break; + +        } + +    } + +    result = (i == size); + + done: + +    va_end(ap); + +    return result; + +} + + +/****************************************************************************** +*                                                                             * +*  Paramètres  : size  = taille de la valeur à considérer pour les travaux.   * +*                hex   = tampon de destination assez grand.                   * +*                value = valeur sur XX bits à transcrire. [OUT]               * +*                                                                             * +*  Description : Traduit une valeur sur XX bits en forme textuelle.           * +*                                                                             * +*  Retour      : Bilan de l'opération.                                        * +*                                                                             * +*  Remarques   : -                                                            * +*                                                                             * +******************************************************************************/ + +bool any_u_to_hex(size_t size, char hex[17], ...) +{ +    bool result;                            /* Bilan à retourner           */ +    va_list ap;                             /* Gestion de l'inconnue       */ +    uint8_t *value8;                        /* Valeur sur 8 bits           */ +    uint16_t *value16;                      /* Valeur sur 16 bits          */ +    uint32_t *value32;                      /* Valeur sur 32 bits          */ +    uint64_t *value64;                      /* Valeur sur 64 bits          */ +    size_t i;                               /* Boucle de parcours #1       */ +    const uint8_t *iter;                    /* Boucle de parcours #2       */ +    uint8_t nibble;                         /* Valeur à retenir            */ + +    result = true; + +    /* Récupération de la destination */ + +    va_start(ap, hex); + +    switch (size) +    { +        case 1: +            value8 = va_arg(ap, uint8_t *); +            iter = (const uint8_t *)value8; +            break; + +        case 2: +            value16 = va_arg(ap, uint16_t *); +            iter = (const uint8_t *)value16; +            break; + +        case 4: +            value32 = va_arg(ap, uint32_t *); +            iter = (const uint8_t *)value32; +            break; + +        case 8: +            value64 = va_arg(ap, uint64_t *); +            iter = (const uint8_t *)value64; +            break; + +        default: +            result = false; +            goto done; +            break; + +    } + +    /* Lecture de la valeur */ + +    for (i = 0; i < size; i++, iter++) +    { +        nibble = (*iter & 0xf0) >> 4; + +        switch (nibble) +        { +            case 0x0 ... 0x9: +                hex[i * 2] = '0' + nibble; +                break; + +            case 0xa ... 0xf: +                hex[i * 2] = 'A' + nibble - 0xa; +                break; + +        } + +        nibble = (*iter & 0x0f); + +        switch (nibble) +        { +            case 0x0 ... 0x9: +                hex[i * 2 + 1] = '0' + nibble; +                break; + +            case 0xa ... 0xf: +                hex[i * 2 + 1] = 'A' + nibble - 0xa; +                break; + +        } + +    } + +    hex[size * 2] = '\0'; + + done: + +    va_end(ap); + +    return result; + +} diff --git a/src/debug/gdbrsp/utils.h b/src/debug/gdbrsp/utils.h new file mode 100644 index 0000000..a67df46 --- /dev/null +++ b/src/debug/gdbrsp/utils.h @@ -0,0 +1,58 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * utils.h - prototypes pour les fonctions qui simplifient la vie dans les interactions avec un serveur GDB + * + * Copyright (C) 2016 Cyrille Bagard + * + *  This file is part of Chrysalide. + * + *  Chrysalide is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 3 of the License, or + *  (at your option) any later version. + * + *  Chrysalide is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with Foobar.  If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _DEBUG_GDBRSP_UTILS_H +#define _DEBUG_GDBRSP_UTILS_H + + +#include <stdbool.h> +#include <stdint.h> +#include <sys/types.h> + + + +/* Indique si les données correspondent à un code d'erreur. */ +bool is_error_code(const char *, size_t); + +/* Relit une valeur sur 8 bits et deux lettres. */ +bool read_fixed_byte(const char *, size_t, uint8_t *); + +/* Traduit en valeur sur XX bits une forme textuelle. */ +bool hex_to_any_u(const char *, size_t, ...); + +#define hex_to_u8(h, v)  hex_to_any_u(h, 1, v) +#define hex_to_u16(h, v) hex_to_any_u(h, 2, v) +#define hex_to_u32(h, v) hex_to_any_u(h, 4, v) +#define hex_to_u64(h, v) hex_to_any_u(h, 8, v) + +/* Traduit une valeur sur XX bits en forme textuelle. */ +bool any_u_to_hex(size_t, char [17], ...); + +#define u8_to_hex(v, h)  any_u_to_hex(1, h, v) +#define u16_to_hex(v, h) any_u_to_hex(2, h, v) +#define u32_to_hex(v, h) any_u_to_hex(4, h, v) +#define u64_to_hex(v, h) any_u_to_hex(8, h, v) + + + +#endif  /* _DEBUG_GDBRSP_UTILS_H */ diff --git a/src/debug/misc.h b/src/debug/misc.h new file mode 100644 index 0000000..dd9492a --- /dev/null +++ b/src/debug/misc.h @@ -0,0 +1,37 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * misc.h - prototypes pour les définitions communes et génériques liées au débogage + * + * Copyright (C) 2017 Cyrille Bagard + * + *  This file is part of Chrysalide. + * + *  Chrysalide is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 3 of the License, or + *  (at your option) any later version. + * + *  Chrysalide is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with Foobar.  If not, see <http://www.gnu.org/licenses/>. + */ + + +#ifndef _DEBUG_MISC_H +#define _DEBUG_MISC_H + + +#include <stdint.h> + + + +/* Couverture maximale des identifiants */ +typedef uint64_t dbg_thread_id_t; + + + +#endif  /* _DEBUG_MISC_H */ diff --git a/src/debug/remgdb/Makefile.am b/src/debug/remgdb/Makefile.am deleted file mode 100644 index 47e5fdc..0000000 --- a/src/debug/remgdb/Makefile.am +++ /dev/null @@ -1,17 +0,0 @@ - -noinst_LTLIBRARIES = libdebugremgdb.la - -libdebugremgdb_la_SOURCES =				\ -	gdb.h gdb.c							\ -	helpers.h helpers.c					\ -	packet.h packet.c					\ -	stream-int.h						\ -	stream.h stream.c					\ -	tcp.h tcp.c - -libdebugremgdb_la_CFLAGS = $(AM_CFLAGS) - - -AM_CPPFLAGS = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS) - -AM_CFLAGS = $(DEBUG_CFLAGS) $(WARNING_FLAGS) $(COMPLIANCE_FLAGS) diff --git a/src/debug/remgdb/gdb.c b/src/debug/remgdb/gdb.c deleted file mode 100644 index 728eee6..0000000 --- a/src/debug/remgdb/gdb.c +++ /dev/null @@ -1,363 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * gdb.c - débogage à l'aide de gdb. - * - * Copyright (C) 2009-2017 Cyrille Bagard - * - *  This file is part of Chrysalide. - * - *  Chrysalide is free software; you can redistribute it and/or modify - *  it under the terms of the GNU General Public License as published by - *  the Free Software Foundation; either version 3 of the License, or - *  (at your option) any later version. - * - *  Chrysalide is distributed in the hope that it will be useful, - *  but WITHOUT ANY WARRANTY; without even the implied warranty of - *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - *  GNU General Public License for more details. - * - *  You should have received a copy of the GNU General Public License - *  along with Foobar.  If not, see <http://www.gnu.org/licenses/>. - */ - - -#include "gdb.h" - - -#include "../debugger-int.h" - - - -#include "helpers.h" -#include "tcp.h" - - - - -/* Débogueur utilisant un serveur GDB (instance) */ -struct _GGdbDebugger -{ -    GBinaryDebugger parent;                 /* A laisser en premier        */ - -    GGdbStream *stream;                     /* Flux de communication       */ - - -#if 0 -    GCond *cond;                            /* Poursuite du déroulement    */ -    GMutex *mutex;                          /* Accès à la condition        */ - -    ptrace_options *options;                /* Configuration du débogage   */ - -    pid_t child;                            /* Processus suivi lancé       */ - -    gboolean run_again;                     /* Reprise du débogage         */ -#endif -}; - -/* Débogueur utilisant un serveur GDB (classe) */ -struct _GGdbDebuggerClass -{ -    GBinaryDebuggerClass parent;            /* A laisser en premier        */ - -}; - - - - - -/* Initialise la classe du débogueur utilisant gdb. */ -static void g_gdb_debugger_class_init(GGdbDebuggerClass *); - -/* Procède à l'initialisation du débogueur utilisant gdb. */ -static void g_gdb_debugger_init(GGdbDebugger *); - - -/* Met en marche le débogueur utilisant un serveur GDB. */ -static bool g_gdb_debugger_run(GGdbDebugger *); - -/* Remet en marche le débogueur utilisant un serveur GDB. */ -static bool g_gdb_debugger_resume(GGdbDebugger *); - -/* Tue le débogueur utilisant un serveur GDB. */ -static bool g_gdb_debugger_kill(GGdbDebugger *); - - - -/* Indique le type défini par la GLib pour le débogueur gdb. */ -G_DEFINE_TYPE(GGdbDebugger, g_gdb_debugger, G_TYPE_BINARY_DEBUGGER); - - - -/****************************************************************************** -*                                                                             * -*  Paramètres  : klass = classe de débogueur à initialiser.                   * -*                                                                             * -*  Description : Initialise la classe du débogueur utilisant gdb.             * -*                                                                             * -*  Retour      : -                                                            * -*                                                                             * -*  Remarques   : -                                                            * -*                                                                             * -******************************************************************************/ - -static void g_gdb_debugger_class_init(GGdbDebuggerClass *klass) -{ - -} - - -/****************************************************************************** -*                                                                             * -*  Paramètres  : debugger = instance de débogueur à préparer.                 * -*                                                                             * -*  Description : Procède à l'initialisation du débogueur utilisant gdb.       * -*                                                                             * -*  Retour      : -                                                            * -*                                                                             * -*  Remarques   : -                                                            * -*                                                                             * -******************************************************************************/ - -static void g_gdb_debugger_init(GGdbDebugger *debugger) -{ -    GBinaryDebugger *parent;                /* Instance parente            */ - -    parent = G_BINARY_DEBUGGER(debugger); - -    parent->run = (basic_debugger_fc)g_gdb_debugger_run; -    parent->resume = (resume_debugger_fc)g_gdb_debugger_resume; -    parent->kill = (basic_debugger_fc)g_gdb_debugger_kill; - -    //parent->get_reg_values = (get_register_values_fc)get_register_values_using_gdb_debugger; - -    //debugger->cond = g_cond_new(); -    //debugger->mutex = g_mutex_new(); - -} - - -/****************************************************************************** -*                                                                             * -*  Paramètres  : binary  = binaire représenter à déboguer.                    * -*                options = paramètres destinés au débogage.                   * -*                                                                             * -*  Description : Crée un débogueur utilisant un serveur GDB distant.          * -*                                                                             * -*  Retour      : Instance de débogueur mise en place ou NULL.                 * -*                                                                             * -*  Remarques   : -                                                            * -*                                                                             * -******************************************************************************/ - -GBinaryDebugger *g_gdb_debugger_new(GLoadedBinary *binary, void *options) -{ -    GBinaryDebugger *result;                /* Débogueur à retourner       */ - -    result = g_object_new(G_TYPE_GDB_DEBUGGER, NULL); - -    return result; - -} - - - - - - - - -/****************************************************************************** -*                                                                             * -*  Paramètres  : debugger = débogueur à lancer.                               * -*                                                                             * -*  Description : Met en marche le débogueur utilisant un serveur GDB.         * -*                                                                             * -*  Retour      : Bilan de l'opération.                                        * -*                                                                             * -*  Remarques   : -                                                            * -*                                                                             * -******************************************************************************/ - -static bool g_gdb_debugger_run(GGdbDebugger *debugger) -{ - - - -    GGdbPacket *packet; - -    bool test; - -    const char *data; -    size_t len; - - -    int sig; -    vmpa_t addr; -    pid_t thread; - - -    debugger->stream = g_gdb_tcp_client_new("127.0.0.1", "6666"); -    if (debugger->stream == NULL) return false; - - -    printf("Connection done !\n"); - - - -    packet = g_gdb_stream_get_free_packet(debugger->stream); - -    g_gdb_packet_start_new_command(packet); -    g_gdb_packet_append(packet, "?"); - - -    test = g_gdb_stream_send_packet(debugger->stream, packet); - - - -    printf(" >> Paquet '%s' bien envoyé ? %s\n", "?", test ? "oui" : "non"); - - - -    g_gdb_stream_mark_packet_as_free(debugger->stream, packet); - -    packet = g_gdb_stream_recv_packet(debugger->stream); - -    g_gdb_packet_get_data(packet, &data, &len, NULL); - -    printf(" << Réception de '%s'\n", data); - - - - - -    get_stop_reply_sig_info(packet, &sig, &addr, &thread, SRE_LITTLE); - -    g_signal_emit_by_name(debugger, "halted", sig, addr, thread); - -         - - - - - -    return true; - -} - - -/****************************************************************************** -*                                                                             * -*  Paramètres  : debugger = débogueur à relancer.                             * -*                                                                             * -*  Description : Remet en marche le débogueur utilisant un serveur GDB.       * -*                                                                             * -*  Retour      : Bilan de l'opération.                                        * -*                                                                             * -*  Remarques   : -                                                            * -*                                                                             * -******************************************************************************/ - -static bool g_gdb_debugger_resume(GGdbDebugger *debugger) -{ - - -    /* -    g_mutex_lock(debugger->mutex); -    debugger->run_again = TRUE; -    g_cond_signal(debugger->cond); -    g_mutex_unlock(debugger->mutex); -    */ -    return true; - -} - - -/****************************************************************************** -*                                                                             * -*  Paramètres  : debugger = débogueur à relancer.                             * -*                                                                             * -*  Description : Tue le débogueur utilisant un serveur GDB.                   * -*                                                                             * -*  Retour      : Bilan de l'opération.                                        * -*                                                                             * -*  Remarques   : -                                                            * -*                                                                             * -******************************************************************************/ - -static bool g_gdb_debugger_kill(GGdbDebugger *debugger) -{ - - -#if 0 -    int ret;                                /* Bilan de l'appel système    */ - -    ret = kill(debugger->child, SIGKILL); -    if (ret != 0) perror("kill"); - -    debugger->child = 0; - -    g_mutex_lock(debugger->mutex); -    debugger->run_again = TRUE; -    g_cond_signal(debugger->cond); -    g_mutex_unlock(debugger->mutex); -#endif -    return true; - -} - - - - - - - - - - - - -void test_gdb(void) -{ - -    GGdbStream *stream; - -    GGdbPacket *packet; - -    bool test; - -    const char *data; -    size_t len; - -    printf("GDB !!!!\n"); - - -    stream = g_gdb_tcp_client_new("192.168.100.141", "6666"); - - - -    packet = g_gdb_stream_get_free_packet(stream); - -    g_gdb_packet_start_new_command(packet); -    g_gdb_packet_append(packet, "g"); - - -    test = g_gdb_stream_send_packet(stream, packet); - - - -    printf(" >> Paquet '%s' bien envoyé ? %s\n", "g", test ? "oui" : "non"); - - - -    g_gdb_stream_mark_packet_as_free(stream, packet); - -    packet = g_gdb_stream_recv_packet(stream); - -    g_gdb_packet_get_data(packet, &data, &len, NULL); - -    printf(" << Réception de '%s'\n", data); - - - - -} | 
